blob: 8f7ce00562ff3598ad432268907b20a05688c495 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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.google.idea.blaze.android.run.runner;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.BuildToolInfo.PathId;
import com.google.idea.blaze.android.sync.sdk.SdkUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.openapi.project.Project;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.jetbrains.android.sdk.AndroidPlatform;
/** A collection of utilities for extracting information from APKs using aapt. */
public final class AaptUtil {
private static final Pattern DEBUGGABLE_PATTERN = Pattern.compile("^application-debuggable$");
private static final Pattern PACKAGE_PATTERN = Pattern.compile("^package: .*name='([\\w\\.]+)'");
private static final Pattern LAUNCHABLE_PATTERN =
Pattern.compile("^launchable-activity: .*name='([\\w\\.]+)'");
/** exception thrown by this class */
public static class AaptUtilException extends Exception {
public AaptUtilException(String message) {
super(message);
}
public AaptUtilException(String message, Throwable cause) {
super(message, cause);
}
}
private AaptUtil() {}
/**
* Determines whether the given APK is debuggable. Trying to debug a non-debuggable APK on a
* release-keys device will fail.
*/
public static boolean isApkDebuggable(Project project, File apk) throws AaptUtilException {
return getAaptBadging(project, apk, DEBUGGABLE_PATTERN) != null;
}
/** Determines the manifest package name for the given APK. */
public static String getApkManifestPackage(Project project, File apk) throws AaptUtilException {
MatchResult packageResult = getAaptBadging(project, apk, PACKAGE_PATTERN);
if (packageResult == null) {
throw new AaptUtilException(
"No match found in `aapt dump badging` for package manifest pattern.");
}
return packageResult.group(1);
}
/** Determines the default launchable activity for the given apk. */
public static String getLaunchableActivity(Project project, File apk) throws AaptUtilException {
MatchResult activityResult = getAaptBadging(project, apk, LAUNCHABLE_PATTERN);
if (activityResult == null) {
throw new AaptUtilException(
"No match found in `aapt dump badging` for launchable activity pattern.");
}
return activityResult.group(1);
}
/**
* Uses aapt to dump badging information for the given apk, and extracts information from the
* output matching the given pattern.
*/
@Nullable
private static MatchResult getAaptBadging(Project project, File apk, Pattern pattern)
throws AaptUtilException {
if (!apk.exists()) {
throw new AaptUtilException("apk file does not exist: " + apk);
}
AndroidPlatform androidPlatform = SdkUtil.getAndroidPlatform(project);
if (androidPlatform == null) {
throw new AaptUtilException(
"Could not find Android platform sdk for project " + project.getName());
}
BuildToolInfo toolInfo = androidPlatform.getSdkData().getLatestBuildTool();
if (toolInfo == null) {
throw new AaptUtilException(
"Could not find Android sdk build-tools for project " + project.getName());
}
String aapt = toolInfo.getPath(PathId.AAPT);
GeneralCommandLine commandLine =
new GeneralCommandLine(aapt, "dump", "badging", apk.getAbsolutePath());
OSProcessHandler handler;
try {
handler = new OSProcessHandler(commandLine);
} catch (ExecutionException e) {
throw new AaptUtilException("Could not execute aapt to extract apk information.", e);
}
// The wrapped stream is closed by the process handler.
BufferedReader reader =
new BufferedReader(new InputStreamReader(handler.getProcess().getInputStream()));
try {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
return matcher.toMatchResult();
}
}
} catch (IOException e) {
throw new AaptUtilException("Could not read aapt output.", e);
}
return null;
}
}