summaryrefslogtreecommitdiff
path: root/cmds
diff options
context:
space:
mode:
Diffstat (limited to 'cmds')
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java3
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java5
-rw-r--r--cmds/appops/OWNERS1
-rw-r--r--cmds/bootanimation/BootAnimation.cpp62
-rw-r--r--cmds/bootanimation/audioplay.cpp26
-rw-r--r--cmds/idmap2/Android.bp21
-rw-r--r--cmds/idmap2/idmap2/CreateMultiple.cpp8
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp4
-rw-r--r--cmds/idmap2/idmap2/Main.cpp8
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp49
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.h15
-rw-r--r--cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl2
-rw-r--r--cmds/idmap2/include/idmap2/BinaryStreamVisitor.h2
-rw-r--r--cmds/idmap2/include/idmap2/FabricatedOverlay.h17
-rw-r--r--cmds/idmap2/include/idmap2/FileUtils.h4
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h7
-rw-r--r--cmds/idmap2/include/idmap2/LogInfo.h2
-rw-r--r--cmds/idmap2/include/idmap2/PrettyPrintVisitor.h2
-rw-r--r--cmds/idmap2/include/idmap2/RawPrintVisitor.h2
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h2
-rw-r--r--cmds/idmap2/include/idmap2/XmlParser.h2
-rw-r--r--cmds/idmap2/libidmap2/CommandLineOptions.cpp8
-rw-r--r--cmds/idmap2/libidmap2/FabricatedOverlay.cpp34
-rw-r--r--cmds/idmap2/libidmap2/FileUtils.cpp24
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp15
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp18
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp10
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp13
-rw-r--r--cmds/idmap2/self_targeting/SelfTargeting.cpp1
-rw-r--r--cmds/idmap2/tests/FabricatedOverlayTests.cpp2
-rw-r--r--cmds/idmap2/tests/FileUtilsTests.cpp5
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp10
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp2
-rw-r--r--cmds/screencap/screencap.cpp10
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java19
-rw-r--r--cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java5
-rw-r--r--cmds/uinput/README.md243
-rw-r--r--cmds/uinput/jni/Android.bp1
-rw-r--r--cmds/uinput/jni/com_android_commands_uinput_Device.cpp48
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Device.java31
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Event.java164
41 files changed, 630 insertions, 277 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index b8d24e388d67..d79131ca5d7c 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -193,6 +193,9 @@ public class Am extends BaseCommand {
instrument.alwaysCheckSignature = true;
} else if (opt.equals("--instrument-sdk-sandbox")) {
instrument.instrumentSdkSandbox = true;
+ } else if (opt.equals("--instrument-sdk-in-sandbox")) {
+ instrument.instrumentSdkSandbox = true;
+ instrument.instrumentSdkInSandbox = true;
} else {
System.err.println("Error: Unknown option: " + opt);
return;
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 2604497dcb63..e60593e8b633 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_INSTRUMENT_SDK_IN_SANDBOX;
import static android.app.ActivityManager.INSTR_FLAG_INSTRUMENT_SDK_SANDBOX;
import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
@@ -99,6 +100,7 @@ public class Instrument {
public String componentNameArg;
public boolean alwaysCheckSignature = false;
public boolean instrumentSdkSandbox = false;
+ public boolean instrumentSdkInSandbox = false;
/**
* Construct the instrument command runner.
@@ -530,6 +532,9 @@ public class Instrument {
if (instrumentSdkSandbox) {
flags |= INSTR_FLAG_INSTRUMENT_SDK_SANDBOX;
}
+ if (instrumentSdkInSandbox) {
+ flags |= INSTR_FLAG_INSTRUMENT_SDK_IN_SANDBOX;
+ }
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/appops/OWNERS b/cmds/appops/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/cmds/appops/OWNERS
+++ b/cmds/appops/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 681f8e9f44a0..89776dba7878 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -16,6 +16,7 @@
#define LOG_NDEBUG 0
#define LOG_TAG "BootAnimation"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <filesystem>
#include <vector>
@@ -29,6 +30,7 @@
#include <math.h>
#include <fcntl.h>
#include <utils/misc.h>
+#include <utils/Trace.h>
#include <signal.h>
#include <time.h>
@@ -80,18 +82,18 @@ static constexpr const char* PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE = "/product
static constexpr const char* OEM_USERSPACE_REBOOT_ANIMATION_FILE = "/oem/media/userspace-reboot.zip";
static constexpr const char* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE = "/system/media/userspace-reboot.zip";
-static const char BOOTANIM_DATA_DIR_PATH[] = "/data/bootanim";
+static const char BOOTANIM_DATA_DIR_PATH[] = "/data/misc/bootanim";
static const char BOOTANIM_TIME_DIR_NAME[] = "time";
-static const char BOOTANIM_TIME_DIR_PATH[] = "/data/bootanim/time";
+static const char BOOTANIM_TIME_DIR_PATH[] = "/data/misc/bootanim/time";
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
-static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/bootanim/time/last_time_change";
+static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/misc/bootanim/time/last_time_change";
static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
-static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/bootanim/time/time_is_accurate";
-static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/bootanim/time/time_format_12_hour";
+static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/misc/bootanim/time/time_is_accurate";
+static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/misc/bootanim/time/time_format_12_hour";
// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
static const long long ACCURATE_TIME_EPOCH = 946684800000;
static constexpr char FONT_BEGIN_CHAR = ' ';
@@ -201,6 +203,7 @@ static GLfloat quadUVs[] = {
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
: Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
+ ATRACE_CALL();
mSession = new SurfaceComposerClient();
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
@@ -214,6 +217,7 @@ BootAnimation::BootAnimation(sp<Callbacks> callbacks)
}
BootAnimation::~BootAnimation() {
+ ATRACE_CALL();
if (mAnimation != nullptr) {
releaseAnimation(mAnimation);
mAnimation = nullptr;
@@ -223,6 +227,7 @@ BootAnimation::~BootAnimation() {
}
void BootAnimation::onFirstRef() {
+ ATRACE_CALL();
status_t err = mSession->linkToComposerDeath(this);
SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
@@ -241,6 +246,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const {
}
void BootAnimation::binderDied(const wp<IBinder>&) {
+ ATRACE_CALL();
// woah, surfaceflinger died!
SLOGD("SurfaceFlinger died, exiting...");
@@ -252,6 +258,7 @@ void BootAnimation::binderDied(const wp<IBinder>&) {
static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo,
bool premultiplyAlpha) {
+ ATRACE_CALL();
AImageDecoder* decoder = nullptr;
AImageDecoder_createFromBuffer(encodedData, dataLength, &decoder);
if (!decoder) {
@@ -283,6 +290,7 @@ static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitm
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
const char* name, bool premultiplyAlpha) {
+ ATRACE_CALL();
Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
if (asset == nullptr)
return NO_INIT;
@@ -339,6 +347,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
status_t BootAnimation::initTexture(FileMap* map, int* width, int* height,
bool premultiplyAlpha) {
+ ATRACE_CALL();
AndroidBitmapInfo bitmapInfo;
void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo,
premultiplyAlpha);
@@ -405,10 +414,12 @@ class BootAnimation::DisplayEventCallback : public LooperCallback {
public:
DisplayEventCallback(BootAnimation* bootAnimation) {
+ ATRACE_CALL();
mBootAnimation = bootAnimation;
}
int handleEvent(int /* fd */, int events, void* /* data */) {
+ ATRACE_CALL();
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x",
events);
@@ -493,6 +504,7 @@ ui::Size BootAnimation::limitSurfaceSize(int width, int height) const {
}
status_t BootAnimation::readyToRun() {
+ ATRACE_CALL();
mAssets.addDefaultAssets();
const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
@@ -629,6 +641,7 @@ status_t BootAnimation::readyToRun() {
}
void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() {
+ ATRACE_CALL();
const auto orientation = parseOrientationProperty();
if (orientation == ui::ROTATION_0) {
@@ -651,6 +664,7 @@ void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() {
}
ui::Rotation BootAnimation::parseOrientationProperty() {
+ ATRACE_CALL();
const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
if (displayIds.size() == 0) {
return ui::ROTATION_0;
@@ -673,11 +687,13 @@ ui::Rotation BootAnimation::parseOrientationProperty() {
}
void BootAnimation::projectSceneToWindow() {
+ ATRACE_CALL();
glViewport(0, 0, mWidth, mHeight);
glScissor(0, 0, mWidth, mHeight);
}
void BootAnimation::resizeSurface(int newWidth, int newHeight) {
+ ATRACE_CALL();
// We assume this function is called on the animation thread.
if (newWidth == mWidth && newHeight == mHeight) {
return;
@@ -705,6 +721,7 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
}
bool BootAnimation::preloadAnimation() {
+ ATRACE_CALL();
findBootAnimationFile();
if (!mZipFileName.empty()) {
mAnimation = loadAnimation(mZipFileName);
@@ -715,6 +732,7 @@ bool BootAnimation::preloadAnimation() {
}
bool BootAnimation::findBootAnimationFileInternal(const std::vector<std::string> &files) {
+ ATRACE_CALL();
for (const std::string& f : files) {
if (access(f.c_str(), R_OK) == 0) {
mZipFileName = f.c_str();
@@ -725,6 +743,7 @@ bool BootAnimation::findBootAnimationFileInternal(const std::vector<std::string>
}
void BootAnimation::findBootAnimationFile() {
+ ATRACE_CALL();
const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
static const std::vector<std::string> bootFiles = {
APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,
@@ -748,6 +767,7 @@ void BootAnimation::findBootAnimationFile() {
}
GLuint compileShader(GLenum shaderType, const GLchar *source) {
+ ATRACE_CALL();
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &source, 0);
glCompileShader(shader);
@@ -766,6 +786,7 @@ GLuint compileShader(GLenum shaderType, const GLchar *source) {
}
GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) {
+ ATRACE_CALL();
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
@@ -781,6 +802,7 @@ GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) {
}
void BootAnimation::initShaders() {
+ ATRACE_CALL();
bool dynamicColoringEnabled = mAnimation != nullptr && mAnimation->dynamicColoringEnabled;
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE);
GLuint imageFragmentShader =
@@ -814,6 +836,7 @@ void BootAnimation::initShaders() {
}
bool BootAnimation::threadLoop() {
+ ATRACE_CALL();
bool result;
initShaders();
@@ -839,6 +862,7 @@ bool BootAnimation::threadLoop() {
}
bool BootAnimation::android() {
+ ATRACE_CALL();
glActiveTexture(GL_TEXTURE0);
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
@@ -906,6 +930,7 @@ bool BootAnimation::android() {
}
void BootAnimation::checkExit() {
+ ATRACE_CALL();
// Allow surface flinger to gracefully request shutdown
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
@@ -916,10 +941,12 @@ void BootAnimation::checkExit() {
}
bool BootAnimation::validClock(const Animation::Part& part) {
+ ATRACE_CALL();
return part.clockPosX != TEXT_MISSING_VALUE && part.clockPosY != TEXT_MISSING_VALUE;
}
bool parseTextCoord(const char* str, int* dest) {
+ ATRACE_CALL();
if (strcmp("c", str) == 0) {
*dest = TEXT_CENTER_VALUE;
return true;
@@ -936,6 +963,7 @@ bool parseTextCoord(const char* str, int* dest) {
// Parse two position coordinates. If only string is non-empty, treat it as the y value.
void parsePosition(const char* str1, const char* str2, int* x, int* y) {
+ ATRACE_CALL();
bool success = false;
if (strlen(str1) == 0) { // No values were specified
// success = false
@@ -964,6 +992,7 @@ void parsePosition(const char* str1, const char* str2, int* x, int* y) {
// If the input string isn't valid, parseColor returns false and color is
// left unchanged.
static bool parseColor(const char str[7], float color[3]) {
+ ATRACE_CALL();
float tmpColor[3];
for (int i = 0; i < 3; i++) {
int val = 0;
@@ -986,6 +1015,7 @@ static bool parseColor(const char str[7], float color[3]) {
// If the input color string is empty, set color with values in defaultColor.
static void parseColorDecimalString(const std::string& colorString,
float color[3], float defaultColor[3]) {
+ ATRACE_CALL();
if (colorString == "") {
memcpy(color, defaultColor, sizeof(float) * 3);
return;
@@ -997,6 +1027,7 @@ static void parseColorDecimalString(const std::string& colorString,
}
static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
+ ATRACE_CALL();
ZipEntryRO entry = zip->findEntryByName(name);
SLOGE_IF(!entry, "couldn't find %s", name);
if (!entry) {
@@ -1019,6 +1050,7 @@ static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
// columns are the printable ASCII characters 0x20 - 0x7f. The
// top row is regular text; the bottom row is bold.
status_t BootAnimation::initFont(Font* font, const char* fallback) {
+ ATRACE_CALL();
status_t status = NO_ERROR;
if (font->map != nullptr) {
@@ -1046,6 +1078,7 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
}
void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
+ ATRACE_CALL();
glEnable(GL_BLEND); // Allow us to draw on top of the animation
glBindTexture(GL_TEXTURE_2D, font.texture.name);
glUseProgram(mTextShader);
@@ -1093,6 +1126,7 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int*
// We render 12 or 24 hour time.
void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) {
+ ATRACE_CALL();
static constexpr char TIME_FORMAT_12[] = "%l:%M";
static constexpr char TIME_FORMAT_24[] = "%H:%M";
static constexpr int TIME_LENGTH = 6;
@@ -1118,6 +1152,7 @@ void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos)
}
void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos) {
+ ATRACE_CALL();
static constexpr int PERCENT_LENGTH = 5;
char percentBuff[PERCENT_LENGTH];
@@ -1130,6 +1165,7 @@ void BootAnimation::drawProgress(int percent, const Font& font, const int xPos,
}
bool BootAnimation::parseAnimationDesc(Animation& animation) {
+ ATRACE_CALL();
String8 desString;
if (!readFile(animation.zip, "desc.txt", desString)) {
@@ -1253,6 +1289,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
}
bool BootAnimation::preloadZip(Animation& animation) {
+ ATRACE_CALL();
// read all the data structures
const size_t pcount = animation.parts.size();
void *cookie = nullptr;
@@ -1358,6 +1395,7 @@ bool BootAnimation::preloadZip(Animation& animation) {
}
bool BootAnimation::movie() {
+ ATRACE_CALL();
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
@@ -1451,6 +1489,7 @@ bool BootAnimation::movie() {
bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part,
const int fadedFramesCount,
const int lastDisplayedProgress) {
+ ATRACE_CALL();
// stop playing only if it is time to exit and it's a partial part which has been faded out
return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount &&
(lastDisplayedProgress == 0 || lastDisplayedProgress == 100);
@@ -1462,6 +1501,7 @@ float mapLinear(float x, float a1, float a2, float b1, float b2) {
}
void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) {
+ ATRACE_CALL();
// Map coordinates from screen space to world space.
float x0 = mapLinear(xStart, 0, mWidth, -1, 1);
float y0 = mapLinear(yStart, 0, mHeight, -1, 1);
@@ -1485,6 +1525,7 @@ void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, fl
}
void BootAnimation::initDynamicColors() {
+ ATRACE_CALL();
for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
const auto syspropName = "persist.bootanim.color" + std::to_string(i + 1);
const auto syspropValue = android::base::GetProperty(syspropName, "");
@@ -1511,6 +1552,7 @@ void BootAnimation::initDynamicColors() {
}
bool BootAnimation::playAnimation(const Animation& animation) {
+ ATRACE_CALL();
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -1724,12 +1766,14 @@ bool BootAnimation::playAnimation(const Animation& animation) {
}
void BootAnimation::processDisplayEvents() {
+ ATRACE_CALL();
// This will poll mDisplayEventReceiver and if there are new events it'll call
// displayEventCallback synchronously.
mLooper->pollOnce(0);
}
void BootAnimation::handleViewport(nsecs_t timestep) {
+ ATRACE_CALL();
if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) {
return;
}
@@ -1772,6 +1816,7 @@ void BootAnimation::handleViewport(nsecs_t timestep) {
}
void BootAnimation::releaseAnimation(Animation* animation) const {
+ ATRACE_CALL();
for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
e = animation->parts.end(); it != e; ++it) {
if (it->animation)
@@ -1783,6 +1828,7 @@ void BootAnimation::releaseAnimation(Animation* animation) const {
}
BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) {
+ ATRACE_CALL();
if (mLoadedFiles.indexOf(fn) >= 0) {
SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
fn.c_str());
@@ -1814,6 +1860,7 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) {
}
bool BootAnimation::updateIsTimeAccurate() {
+ ATRACE_CALL();
static constexpr long long MAX_TIME_IN_PAST = 60000LL * 60LL * 24LL * 30LL; // 30 days
static constexpr long long MAX_TIME_IN_FUTURE = 60000LL * 90LL; // 90 minutes
@@ -1857,11 +1904,13 @@ BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) :
mInotifyFd(-1), mBootAnimWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
BootAnimation::TimeCheckThread::~TimeCheckThread() {
+ ATRACE_CALL();
// mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
close(mInotifyFd);
}
bool BootAnimation::TimeCheckThread::threadLoop() {
+ ATRACE_CALL();
bool shouldLoop = doThreadLoop() && !mBootAnimation->mTimeIsAccurate
&& mBootAnimation->mClockEnabled;
if (!shouldLoop) {
@@ -1872,6 +1921,7 @@ bool BootAnimation::TimeCheckThread::threadLoop() {
}
bool BootAnimation::TimeCheckThread::doThreadLoop() {
+ ATRACE_CALL();
static constexpr int BUFF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1));
// Poll instead of doing a blocking read so the Thread can exit if requested.
@@ -1909,6 +1959,7 @@ bool BootAnimation::TimeCheckThread::doThreadLoop() {
}
void BootAnimation::TimeCheckThread::addTimeDirWatch() {
+ ATRACE_CALL();
mTimeWd = inotify_add_watch(mInotifyFd, BOOTANIM_TIME_DIR_PATH,
IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
if (mTimeWd > 0) {
@@ -1919,6 +1970,7 @@ void BootAnimation::TimeCheckThread::addTimeDirWatch() {
}
status_t BootAnimation::TimeCheckThread::readyToRun() {
+ ATRACE_CALL();
mInotifyFd = inotify_init();
if (mInotifyFd < 0) {
SLOGE("Could not initialize inotify fd");
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
index c5e16c6b7deb..9b95b04149bf 100644
--- a/cmds/bootanimation/audioplay.cpp
+++ b/cmds/bootanimation/audioplay.cpp
@@ -20,6 +20,8 @@
#define CHATTY ALOGD
#define LOG_TAG "audioplay"
+#include <binder/IServiceManager.h>
+
#include "audioplay.h"
#include <string.h>
@@ -316,8 +318,13 @@ public:
: Thread(false),
mExampleAudioData(exampleAudioData),
mExampleAudioLength(exampleAudioLength) {}
+
private:
virtual bool threadLoop() {
+ if (defaultServiceManager()->checkService(String16("audio")) == nullptr) {
+ ALOGW("Audio service is not ready yet, ignore creating playback engine");
+ return false;
+ }
audioplay::create(mExampleAudioData, mExampleAudioLength);
// Exit immediately
return false;
@@ -334,6 +341,11 @@ class AudioAnimationCallbacks : public android::BootAnimation::Callbacks {
public:
void init(const Vector<Animation::Part>& parts) override {
const Animation::Part* partWithAudio = nullptr;
+
+ if (!playSoundsAllowed()) {
+ return;
+ }
+
for (const Animation::Part& part : parts) {
if (part.audioData != nullptr) {
partWithAudio = &part;
@@ -401,14 +413,14 @@ bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
}
bool playClip(const uint8_t* buf, int size) {
- // Parse the WAV header
- const ChunkFormat* chunkFormat;
- if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
+ if (!hasPlayer()) {
+ ALOGE("cannot play clip %p without a player", buf);
return false;
}
- if (!hasPlayer()) {
- ALOGD("cannot play clip %p without a player", buf);
+ // Parse the WAV header
+ const ChunkFormat* chunkFormat;
+ if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
return false;
}
@@ -433,11 +445,9 @@ bool playClip(const uint8_t* buf, int size) {
void setPlaying(bool isPlaying) {
if (!hasPlayer()) return;
- SLresult result;
-
if (nullptr != bqPlayerPlay) {
// set the player's state
- result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
+ (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 5f06c971ba98..55ec7dae16b1 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -28,11 +28,14 @@ cc_defaults {
tidy_checks: [
"modernize-*",
"-modernize-avoid-c-arrays",
+ "-modernize-use-nodiscard",
"-modernize-use-trailing-return-type",
"android-*",
"misc-*",
+ "-misc-const-correctness",
"readability-*",
"-readability-identifier-length",
+ "-readability-implicit-bool-conversion",
],
tidy_checks_as_errors: [
"modernize-*",
@@ -56,6 +59,7 @@ cc_defaults {
"-readability-const-return-type",
"-readability-convert-member-functions-to-static",
"-readability-duplicate-include",
+ "-readability-implicit-bool-conversion",
"-readability-else-after-return",
"-readability-named-parameter",
"-readability-redundant-access-specifiers",
@@ -80,13 +84,13 @@ cc_library {
enabled: false,
},
static_libs: [
+ "libidmap2_policies",
"libidmap2_protos",
],
shared_libs: [
"libandroidfw",
"libbase",
"libcutils",
- "libidmap2_policies",
"libprotobuf-cpp-lite",
"libutils",
"libz",
@@ -125,7 +129,7 @@ cc_library {
},
}
-cc_library {
+cc_library_static {
name: "libidmap2_policies",
defaults: [
"idmap2_defaults",
@@ -142,9 +146,6 @@ cc_library {
],
},
host: {
- shared: {
- enabled: false,
- },
static_libs: [
"libandroidfw",
],
@@ -191,7 +192,6 @@ cc_test {
"libandroidfw",
"libbase",
"libidmap2",
- "libidmap2_policies",
"liblog",
"libprotobuf-cpp-lite",
"libutils",
@@ -199,6 +199,9 @@ cc_test {
"libz",
"libziparchive",
],
+ static_libs: [
+ "libidmap2_policies",
+ ],
},
host: {
static_libs: [
@@ -255,12 +258,14 @@ cc_binary {
"libbase",
"libcutils",
"libidmap2",
- "libidmap2_policies",
"libprotobuf-cpp-lite",
"libutils",
"libz",
"libziparchive",
],
+ static_libs: [
+ "libidmap2_policies",
+ ],
},
host: {
static_libs: [
@@ -298,13 +303,13 @@ cc_binary {
"libbinder",
"libcutils",
"libidmap2",
- "libidmap2_policies",
"libprotobuf-cpp-lite",
"libutils",
"libziparchive",
],
static_libs: [
"libc++fs",
+ "libidmap2_policies",
"libidmap2_protos",
"libidmap2daidl",
],
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 953d99f8dcfb..68800cde6101 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -18,8 +18,8 @@
#include <sys/types.h> // umask
#include <fstream>
+#include <iostream>
#include <memory>
-#include <ostream>
#include <string>
#include <vector>
@@ -51,7 +51,7 @@ using android::idmap2::utils::UidHasWriteAccessToPath;
Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
SYSTRACE << "CreateMultiple " << args;
std::string target_apk_path;
- std::string idmap_dir = kIdmapCacheDir;
+ std::string idmap_dir{kIdmapCacheDir};
std::vector<std::string> overlay_apk_paths;
std::vector<std::string> policies;
bool ignore_overlayable = false;
@@ -67,7 +67,7 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
.OptionalOption("--idmap-dir",
StringPrintf("output: path to the directory in which to write idmap file"
" (defaults to %s)",
- kIdmapCacheDir),
+ kIdmapCacheDir.data()),
&idmap_dir)
.OptionalOption("--policy",
"input: an overlayable policy this overlay fulfills"
@@ -142,7 +142,7 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
}
for (const std::string& idmap_path : idmap_paths) {
- std::cout << idmap_path << std::endl;
+ std::cout << idmap_path << '\n';
}
return Unit{};
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 928d21ff3a12..34d91795fc29 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -16,9 +16,9 @@
#include <algorithm>
#include <fstream>
+#include <iostream>
#include <iterator>
#include <memory>
-#include <ostream>
#include <string>
#include <utility>
#include <vector>
@@ -230,7 +230,7 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
if (!value) {
return Error(value.GetError(), "resource 0x%08x not found", *resid);
}
- std::cout << *value << std::endl;
+ std::cout << *value << '\n';
}
return Unit{};
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index aa6d0e76698f..5ef15a684853 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -45,7 +45,7 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
}
out << iter->first;
}
- out << "]" << std::endl;
+ out << "]" << '\n';
}
} // namespace
@@ -65,18 +65,18 @@ int main(int argc, char** argv) {
const std::unique_ptr<std::vector<std::string>> args =
CommandLineOptions::ConvertArgvToVector(argc - 1, const_cast<const char**>(argv + 1));
if (!args) {
- std::cerr << "error: failed to parse command line options" << std::endl;
+ std::cerr << "error: failed to parse command line options" << '\n';
return EXIT_FAILURE;
}
const auto iter = commands.find(argv[1]);
if (iter == commands.end()) {
- std::cerr << argv[1] << ": command not found" << std::endl;
+ std::cerr << argv[1] << ": command not found" << '\n';
PrintUsage(commands, std::cerr);
return EXIT_FAILURE;
}
const auto result = iter->second(*args);
if (!result) {
- std::cerr << "error: " << result.GetErrorMessage() << std::endl;
+ std::cerr << "error: " << result.GetErrorMessage() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 10947dc90a76..d76ca5bdce42 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -59,7 +59,7 @@ using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask
namespace {
-constexpr const char* kFrameworkPath = "/system/framework/framework-res.apk";
+constexpr std::string_view kFrameworkPath = "/system/framework/framework-res.apk";
Status ok() {
return Status::ok();
@@ -207,23 +207,47 @@ Status Idmap2Service::createIdmap(const std::string& target_path, const std::str
idmap2::Result<Idmap2Service::TargetResourceContainerPtr> Idmap2Service::GetTargetContainer(
const std::string& target_path) {
- if (target_path == kFrameworkPath) {
- if (framework_apk_cache_ == nullptr) {
- // Initialize the framework APK cache.
- auto target = TargetResourceContainer::FromPath(target_path);
- if (!target) {
- return target.GetError();
+ const bool is_framework = target_path == kFrameworkPath;
+ bool use_cache;
+ struct stat st = {};
+ if (is_framework || !::stat(target_path.c_str(), &st)) {
+ use_cache = true;
+ } else {
+ LOG(WARNING) << "failed to stat target path '" << target_path << "' for the cache";
+ use_cache = false;
+ }
+
+ if (use_cache) {
+ std::lock_guard lock(container_cache_mutex_);
+ if (auto cache_it = container_cache_.find(target_path); cache_it != container_cache_.end()) {
+ const auto& item = cache_it->second;
+ if (is_framework ||
+ (item.dev == st.st_dev && item.inode == st.st_ino && item.size == st.st_size
+ && item.mtime.tv_sec == st.st_mtim.tv_sec && item.mtime.tv_nsec == st.st_mtim.tv_nsec)) {
+ return {item.apk.get()};
}
- framework_apk_cache_ = std::move(*target);
+ container_cache_.erase(cache_it);
}
- return {framework_apk_cache_.get()};
}
auto target = TargetResourceContainer::FromPath(target_path);
if (!target) {
return target.GetError();
}
- return {std::move(*target)};
+ if (!use_cache) {
+ return {std::move(*target)};
+ }
+
+ const auto res = target->get();
+ std::lock_guard lock(container_cache_mutex_);
+ container_cache_.emplace(target_path, CachedContainer {
+ .dev = dev_t(st.st_dev),
+ .inode = ino_t(st.st_ino),
+ .size = st.st_size,
+ .mtime = st.st_mtim,
+ .apk = std::move(*target)
+ });
+ return {res};
}
Status Idmap2Service::createFabricatedOverlay(
@@ -241,7 +265,8 @@ Status Idmap2Service::createFabricatedOverlay(
res.configuration.value_or(std::string()));
} else if (res.binaryData.has_value()) {
builder.SetResourceValue(res.resourceName, res.binaryData->get(),
- res.configuration.value_or(std::string()));
+ res.binaryDataOffset, res.binaryDataSize,
+ res.configuration.value_or(std::string()));
} else {
builder.SetResourceValue(res.resourceName, res.dataType, res.data,
res.configuration.value_or(std::string()));
@@ -257,7 +282,7 @@ Status Idmap2Service::createFabricatedOverlay(
const std::string random_suffix = RandomStringForPath(kSuffixLength);
file_name = StringPrintf("%s-%s-%s.frro", overlay.packageName.c_str(),
overlay.overlayName.c_str(), random_suffix.c_str());
- path = StringPrintf("%s/%s", kIdmapCacheDir, file_name.c_str());
+ path = StringPrintf("%s/%s", kIdmapCacheDir.data(), file_name.c_str());
// Invoking std::filesystem::exists with a file name greater than 255 characters will cause this
// process to abort since the name exceeds the maximum file name size.
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index cc8cc5f218d2..a69fa6119974 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -75,7 +75,20 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
private:
// idmap2d is killed after a period of inactivity, so any information stored on this class should
// be able to be recalculated if idmap2 dies and restarts.
- std::unique_ptr<idmap2::TargetResourceContainer> framework_apk_cache_;
+
+ // A cache item for the resource containers (apks or frros), with all information needed to
+ // detect if it has changed since it was parsed:
+ // - (dev, inode) pair uniquely identifies a file on a particular device partition (see stat(2)).
+ // - (mtime, size) ensure the file data hasn't changed inside that file.
+ struct CachedContainer {
+ dev_t dev;
+ ino_t inode;
+ int64_t size;
+ struct timespec mtime;
+ std::unique_ptr<idmap2::TargetResourceContainer> apk;
+ };
+ std::unordered_map<std::string, CachedContainer> container_cache_;
+ std::mutex container_cache_mutex_;
int32_t frro_iter_id_ = 0;
std::optional<std::filesystem::directory_iterator> frro_iter_;
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
index 3ad6d58e8253..8ebd454705f0 100644
--- a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
@@ -26,4 +26,6 @@ parcelable FabricatedOverlayInternalEntry {
@nullable @utf8InCpp String stringData;
@nullable ParcelFileDescriptor binaryData;
@nullable @utf8InCpp String configuration;
+ long binaryDataOffset;
+ long binaryDataSize;
} \ No newline at end of file
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 7b38bd11d847..57af1b61c300 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -18,7 +18,7 @@
#define IDMAP2_INCLUDE_IDMAP2_BINARYSTREAMVISITOR_H_
#include <cstdint>
-#include <iostream>
+#include <ostream>
#include <string>
#include "idmap2/Idmap.h"
diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
index 9f57710edb0b..1e7d4c28f45c 100644
--- a/cmds/idmap2/include/idmap2/FabricatedOverlay.h
+++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
@@ -19,9 +19,10 @@
#include <libidmap2/proto/fabricated_v1.pb.h>
-#include <iostream>
+#include <istream>
#include <map>
#include <memory>
+#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -48,6 +49,8 @@ struct FabricatedOverlay {
Builder& SetResourceValue(const std::string& resource_name,
std::optional<android::base::borrowed_fd>&& binary_value,
+ off64_t data_binary_offset,
+ size_t data_binary_size,
const std::string& configuration);
inline Builder& setFrroPath(std::string frro_path) {
@@ -64,6 +67,8 @@ struct FabricatedOverlay {
DataValue data_value;
std::string data_string_value;
std::optional<android::base::borrowed_fd> data_binary_value;
+ off64_t data_binary_offset;
+ size_t data_binary_size;
std::string configuration;
};
@@ -75,6 +80,12 @@ struct FabricatedOverlay {
std::vector<Entry> entries_;
};
+ struct BinaryData {
+ android::base::borrowed_fd file_descriptor;
+ off64_t offset;
+ size_t size;
+ };
+
Result<Unit> ToBinaryStream(std::ostream& stream) const;
static Result<FabricatedOverlay> FromBinaryStream(std::istream& stream);
@@ -91,13 +102,13 @@ struct FabricatedOverlay {
explicit FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::string&& string_pool_data_,
- std::vector<android::base::borrowed_fd> binary_files_,
+ std::vector<FabricatedOverlay::BinaryData> binary_files_,
off_t total_binary_bytes_,
std::optional<uint32_t> crc_from_disk = {});
pb::FabricatedOverlay overlay_pb_;
std::string string_pool_data_;
- std::vector<android::base::borrowed_fd> binary_files_;
+ std::vector<FabricatedOverlay::BinaryData> binary_files_;
uint32_t total_binary_bytes_;
std::optional<uint32_t> crc_from_disk_;
mutable std::optional<SerializedData> data_;
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index bc0bb47fc2b7..3e99981b4e0d 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -19,12 +19,12 @@
#include <sys/types.h>
-#include <random>
#include <string>
+#include <string_view>
namespace android::idmap2::utils {
-constexpr const char* kIdmapCacheDir = "/data/resource-cache";
+constexpr std::string_view kIdmapCacheDir = "/data/resource-cache";
constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 03e714a3847e..e86f81485a41 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -71,9 +71,10 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
#define IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
-#include <iostream>
+#include <istream>
#include <memory>
#include <string>
+#include <string_view>
#include <vector>
#include "android-base/macros.h"
@@ -272,8 +273,8 @@ class IdmapData {
class Idmap {
public:
- static std::string CanonicalIdmapPathFor(const std::string& absolute_dir,
- const std::string& absolute_apk_path);
+ static std::string CanonicalIdmapPathFor(std::string_view absolute_dir,
+ std::string_view absolute_apk_path);
static Result<std::unique_ptr<const Idmap>> FromBinaryStream(std::istream& stream);
diff --git a/cmds/idmap2/include/idmap2/LogInfo.h b/cmds/idmap2/include/idmap2/LogInfo.h
index a6237e6f6ba9..b57615270d50 100644
--- a/cmds/idmap2/include/idmap2/LogInfo.h
+++ b/cmds/idmap2/include/idmap2/LogInfo.h
@@ -61,7 +61,7 @@ class LogInfo {
#ifdef __ANDROID__
LOG(WARNING) << msg.GetString();
#else
- std::cerr << "W " << msg.GetString() << std::endl;
+ std::cerr << "W " << msg.GetString() << '\n';
#endif
lines_.push_back("W " + msg.GetString());
}
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index 4464201a1f2e..ed18d9cbf20f 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -17,7 +17,7 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
#define IDMAP2_INCLUDE_IDMAP2_PRETTYPRINTVISITOR_H_
-#include <iostream>
+#include <ostream>
#include <memory>
#include "androidfw/AssetManager2.h"
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index ebd0d1eb2fbc..849ba11aacff 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -17,7 +17,7 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
#define IDMAP2_INCLUDE_IDMAP2_RAWPRINTVISITOR_H_
-#include <iostream>
+#include <ostream>
#include <memory>
#include <string>
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index c2b0abed442c..d4490ef47b25 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -43,6 +43,8 @@ struct TargetValue {
DataValue data_value;
std::string data_string_value;
std::optional<android::base::borrowed_fd> data_binary_value;
+ off64_t data_binary_offset;
+ size_t data_binary_size;
};
struct TargetValueWithConfig {
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
index c968a5e6c04f..c93b06738fcb 100644
--- a/cmds/idmap2/include/idmap2/XmlParser.h
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -17,8 +17,6 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
-#include <iostream>
-#include <map>
#include <memory>
#include <string>
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index b1edc1891541..2da05b36b775 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -19,9 +19,9 @@
#include <algorithm>
#include <cassert>
#include <iomanip>
-#include <iostream>
#include <iterator>
#include <memory>
+#include <ostream>
#include <set>
#include <sstream>
#include <string>
@@ -132,7 +132,7 @@ Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) con
separator = true;
stream << opt << ": missing mandatory option";
}
- stream << std::endl;
+ stream << '\n';
Usage(stream);
return Error("%s", stream.str().c_str());
}
@@ -169,7 +169,7 @@ void CommandLineOptions::Usage(std::ostream& out) const {
out << " [" << opt.name << " arg [..]]";
}
}
- out << std::endl << std::endl;
+ out << "\n\n";
for (const Option& opt : options_) {
out << std::left << std::setw(maxLength);
if (opt.argument) {
@@ -182,7 +182,7 @@ void CommandLineOptions::Usage(std::ostream& out) const {
opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
out << " (can be provided multiple times)";
}
- out << std::endl;
+ out << '\n';
}
}
diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
index dd5be21cd164..47daf23c6381 100644
--- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
+++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
@@ -55,7 +55,7 @@ void Write32(std::ostream& stream, uint32_t value) {
FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::string&& string_pool_data,
- std::vector<android::base::borrowed_fd> binary_files,
+ std::vector<FabricatedOverlay::BinaryData> binary_files,
off_t total_binary_bytes,
std::optional<uint32_t> crc_from_disk)
: overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)),
@@ -81,7 +81,7 @@ FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, uint32_t data_value,
const std::string& configuration) {
entries_.emplace_back(
- Entry{resource_name, data_type, data_value, "", std::nullopt, configuration});
+ Entry{resource_name, data_type, data_value, "", std::nullopt, 0, 0, configuration});
return *this;
}
@@ -89,14 +89,15 @@ FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, const std::string& data_string_value,
const std::string& configuration) {
entries_.emplace_back(
- Entry{resource_name, data_type, 0, data_string_value, std::nullopt, configuration});
+ Entry{resource_name, data_type, 0, data_string_value, std::nullopt, 0, 0, configuration});
return *this;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, std::optional<android::base::borrowed_fd>&& binary_value,
- const std::string& configuration) {
- entries_.emplace_back(Entry{resource_name, 0, 0, "", binary_value, configuration});
+ off64_t data_binary_offset, size_t data_binary_size, const std::string& configuration) {
+ entries_.emplace_back(Entry{resource_name, 0, 0, "", binary_value,
+ data_binary_offset, data_binary_size, configuration});
return *this;
}
@@ -148,7 +149,8 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
}
value->second = TargetValue{res_entry.data_type, res_entry.data_value,
- res_entry.data_string_value, res_entry.data_binary_value};
+ res_entry.data_string_value, res_entry.data_binary_value,
+ res_entry.data_binary_offset, res_entry.data_binary_size};
}
pb::FabricatedOverlay overlay_pb;
@@ -157,7 +159,7 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
overlay_pb.set_target_package_name(target_package_name_);
overlay_pb.set_target_overlayable(target_overlayable_);
- std::vector<android::base::borrowed_fd> binary_files;
+ std::vector<FabricatedOverlay::BinaryData> binary_files;
size_t total_binary_bytes = 0;
// 16 for the number of bytes in the frro file before the binary data
const size_t FRRO_HEADER_SIZE = 16;
@@ -182,16 +184,15 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
pb_value->set_data_value(ref.index());
} else if (value.second.data_binary_value.has_value()) {
pb_value->set_data_type(Res_value::TYPE_STRING);
- struct stat s;
- if (fstat(value.second.data_binary_value->get(), &s) == -1) {
- return Error("unable to get size of binary file: %d", errno);
- }
std::string uri
= StringPrintf("frro:/%s?offset=%d&size=%d", frro_path_.c_str(),
static_cast<int> (FRRO_HEADER_SIZE + total_binary_bytes),
- static_cast<int> (s.st_size));
- total_binary_bytes += s.st_size;
- binary_files.emplace_back(value.second.data_binary_value->get());
+ static_cast<int> (value.second.data_binary_size));
+ total_binary_bytes += value.second.data_binary_size;
+ binary_files.emplace_back(FabricatedOverlay::BinaryData{
+ value.second.data_binary_value->get(),
+ value.second.data_binary_offset,
+ value.second.data_binary_size});
auto ref = string_pool.MakeRef(std::move(uri));
pb_value->set_data_value(ref.index());
} else {
@@ -310,8 +311,9 @@ Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const {
Write32(stream, (*data)->pb_crc);
Write32(stream, total_binary_bytes_);
std::string file_contents;
- for (const android::base::borrowed_fd fd : binary_files_) {
- if (!ReadFdToString(fd, &file_contents)) {
+ for (const FabricatedOverlay::BinaryData fd : binary_files_) {
+ file_contents.resize(fd.size);
+ if (!ReadFullyAtOffset(fd.file_descriptor, file_contents.data(), fd.size, fd.offset)) {
return Error("Failed to read binary file data.");
}
stream.write(file_contents.data(), file_contents.length());
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 98a4ceabdb94..bc5654aad7b8 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -16,11 +16,13 @@
#include "idmap2/FileUtils.h"
+#include <random>
#include <string>
+#include <string_view>
#include "android-base/file.h"
#include "android-base/macros.h"
-#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "private/android_filesystem_config.h"
namespace android::idmap2::utils {
@@ -33,9 +35,9 @@ bool UidHasWriteAccessToPath(uid_t uid, const std::string& path) {
return false;
}
- const std::string cache_subdir = base::StringPrintf("%s/", kIdmapCacheDir);
- if (canonical_path == kIdmapCacheDir ||
- canonical_path.compare(0, cache_subdir.size(), cache_subdir) == 0) {
+ if (base::StartsWith(canonical_path, kIdmapCacheDir) &&
+ (canonical_path.size() == kIdmapCacheDir.size() ||
+ canonical_path[kIdmapCacheDir.size()] == '/')) {
// limit access to /data/resource-cache to root and system
return uid == AID_ROOT || uid == AID_SYSTEM;
}
@@ -47,17 +49,17 @@ bool UidHasWriteAccessToPath(uid_t uid ATTRIBUTE_UNUSED, const std::string& path
}
#endif
-std::string RandomStringForPath(const size_t length) {
- constexpr char kChars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- constexpr size_t kCharLastIndex = sizeof(kChars) - 1;
+std::string RandomStringForPath(size_t length) {
+ constexpr std::string_view kChars =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string out_rand;
- out_rand.reserve(length);
+ out_rand.resize(length);
- std::random_device rd;
- std::uniform_int_distribution<int> dist(0, kCharLastIndex);
+ static thread_local std::random_device rd;
+ std::uniform_int_distribution<int> dist(0, kChars.size() - 1);
for (size_t i = 0; i < length; i++) {
- out_rand[i] = kChars[dist(rd) % (kCharLastIndex)];
+ out_rand[i] = kChars[dist(rd)];
}
return out_rand;
}
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 7c0b937122c7..12d9dd9bd1ad 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -18,13 +18,14 @@
#include <algorithm>
#include <cassert>
-#include <iostream>
+#include <istream>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <utility>
+#include "android-base/format.h"
#include "android-base/macros.h"
#include "androidfw/AssetManager2.h"
#include "idmap2/ResourceMapping.h"
@@ -80,7 +81,7 @@ bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) {
if (padding_size != 0 && !stream.seekg(padding_size, std::ios_base::cur)) {
return false;
}
- *out = buf;
+ *out = std::move(buf);
return true;
}
@@ -279,13 +280,13 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea
return std::move(data);
}
-std::string Idmap::CanonicalIdmapPathFor(const std::string& absolute_dir,
- const std::string& absolute_apk_path) {
+std::string Idmap::CanonicalIdmapPathFor(std::string_view absolute_dir,
+ std::string_view absolute_apk_path) {
assert(absolute_dir.size() > 0 && absolute_dir[0] == "/");
assert(absolute_apk_path.size() > 0 && absolute_apk_path[0] == "/");
- std::string copy(++absolute_apk_path.cbegin(), absolute_apk_path.cend());
+ std::string copy(absolute_apk_path.begin() + 1, absolute_apk_path.end());
replace(copy.begin(), copy.end(), '/', '@');
- return absolute_dir + "/" + copy + "@idmap";
+ return fmt::format("{}/{}@idmap", absolute_dir, copy);
}
Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& stream) {
@@ -332,7 +333,7 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
values[cd] = value;
inline_value_count++;
}
- data->target_inline_entries_.push_back({mapping.first, values});
+ data->target_inline_entries_.push_back({mapping.first, std::move(values)});
}
}
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index a44fa756aa1c..eb9458268dad 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -34,21 +34,21 @@ void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
}
void PrettyPrintVisitor::visit(const IdmapHeader& header) {
- stream_ << "Paths:" << std::endl
- << TAB "target path : " << header.GetTargetPath() << std::endl
- << TAB "overlay path : " << header.GetOverlayPath() << std::endl;
+ stream_ << "Paths:" << '\n'
+ << TAB "target path : " << header.GetTargetPath() << '\n'
+ << TAB "overlay path : " << header.GetOverlayPath() << '\n';
if (!header.GetOverlayName().empty()) {
- stream_ << "Overlay name: " << header.GetOverlayName() << std::endl;
+ stream_ << "Overlay name: " << header.GetOverlayName() << '\n';
}
const std::string& debug = header.GetDebugInfo();
if (!debug.empty()) {
std::istringstream debug_stream(debug);
std::string line;
- stream_ << "Debug info:" << std::endl;
+ stream_ << "Debug info:" << '\n';
while (std::getline(debug_stream, line)) {
- stream_ << TAB << line << std::endl;
+ stream_ << TAB << line << '\n';
}
}
@@ -59,7 +59,7 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
overlay_ = std::move(*overlay);
}
- stream_ << "Mapping:" << std::endl;
+ stream_ << "Mapping:" << '\n';
}
void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) {
@@ -90,7 +90,7 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
<< base::StringPrintf("0x%08x -> 0x%08x (%s -> %s)", target_entry.target_id,
target_entry.overlay_id, target_name.c_str(),
overlay_name.c_str())
- << std::endl;
+ << '\n';
}
for (auto& target_entry : data.GetTargetInlineEntries()) {
@@ -114,7 +114,7 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
}
}
- stream_ << " (" << target_name << ")" << std::endl;
+ stream_ << " (" << target_name << ")" << '\n';
}
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index bbef5bff13f4..9d04a7f87400 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -161,7 +161,7 @@ void RawPrintVisitor::print(uint8_t value, const char* fmt, ...) {
va_end(ap);
stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment
- << std::endl;
+ << '\n';
offset_ += sizeof(uint8_t);
}
@@ -173,7 +173,7 @@ void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) {
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
- stream_ << base::StringPrintf("%08zx: %04x", offset_, value) << " " << comment << std::endl;
+ stream_ << base::StringPrintf("%08zx: %04x", offset_, value) << " " << comment << '\n';
offset_ += sizeof(uint16_t);
}
@@ -185,7 +185,7 @@ void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) {
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
- stream_ << base::StringPrintf("%08zx: %08x", offset_, value) << " " << comment << std::endl;
+ stream_ << base::StringPrintf("%08zx: %08x", offset_, value) << " " << comment << '\n';
offset_ += sizeof(uint32_t);
}
@@ -198,7 +198,7 @@ void RawPrintVisitor::print(const std::string& value, bool print_value, const ch
va_end(ap);
stream_ << base::StringPrintf("%08zx: %08x", offset_, (uint32_t)value.size()) << " " << comment
- << " size" << std::endl;
+ << " size" << '\n';
offset_ += sizeof(uint32_t);
stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment;
@@ -207,7 +207,7 @@ void RawPrintVisitor::print(const std::string& value, bool print_value, const ch
if (print_value) {
stream_ << ": " << value;
}
- stream_ << std::endl;
+ stream_ << '\n';
}
void RawPrintVisitor::align() {
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index f71e6b95ba32..780715561006 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -16,8 +16,6 @@
#include "idmap2/XmlParser.h"
-#include <iostream>
-#include <map>
#include <memory>
#include <string>
#include <utility>
@@ -132,11 +130,14 @@ Result<Res_value> XmlParser::Node::GetAttributeValue(ResourceId attr,
}
Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
+ String16 name16;
return FindAttribute(parser_, name, [&](size_t index) -> bool {
- size_t len;
- const String16 key16(parser_.getAttributeName(index, &len));
- std::string key = String8(key16).c_str();
- return key == name;
+ if (name16.empty()) {
+ name16 = String16(name.c_str(), name.size());
+ }
+ size_t key_len;
+ const auto key16 = parser_.getAttributeName(index, &key_len);
+ return key16 && name16.size() == key_len && name16 == key16;
});
}
diff --git a/cmds/idmap2/self_targeting/SelfTargeting.cpp b/cmds/idmap2/self_targeting/SelfTargeting.cpp
index a8aa03309b16..c7f5cf3632c5 100644
--- a/cmds/idmap2/self_targeting/SelfTargeting.cpp
+++ b/cmds/idmap2/self_targeting/SelfTargeting.cpp
@@ -52,6 +52,7 @@ CreateFrroFile(std::string& out_err_result, const std::string& packageName,
const auto dataType = entry_params.data_type;
if (entry_params.data_binary_value.has_value()) {
builder.SetResourceValue(entry_params.resource_name, *entry_params.data_binary_value,
+ entry_params.binary_data_offset, entry_params.binary_data_size,
entry_params.configuration);
} else if (dataType >= Res_value::TYPE_FIRST_INT && dataType <= Res_value::TYPE_LAST_INT) {
builder.SetResourceValue(entry_params.resource_name, dataType,
diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
index e13a0eb5d488..b460bb33f559 100644
--- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp
+++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
@@ -59,7 +59,7 @@ TEST(FabricatedOverlayTests, SetResourceValue) {
Res_value::TYPE_STRING,
"foobar",
"en-rUS-normal-xxhdpi-v21")
- .SetResourceValue("com.example.target:drawable/dr1", fd, "port-xxhdpi-v7")
+ .SetResourceValue("com.example.target:drawable/dr1", fd, 0, 8341, "port-xxhdpi-v7")
.setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(overlay);
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index 5750ca1f49c5..b160e8e08618 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -27,8 +27,9 @@ namespace android::idmap2::utils {
#ifdef __ANDROID__
TEST(FileUtilsTests, UidHasWriteAccessToPath) {
constexpr const char* tmp_path = "/data/local/tmp/test@idmap";
- const std::string cache_path(base::StringPrintf("%s/test@idmap", kIdmapCacheDir));
- const std::string sneaky_cache_path(base::StringPrintf("/data/../%s/test@idmap", kIdmapCacheDir));
+ const std::string cache_path(base::StringPrintf("%s/test@idmap", kIdmapCacheDir.data()));
+ const std::string sneaky_cache_path(
+ base::StringPrintf("/data/../%s/test@idmap", kIdmapCacheDir.data()));
ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, tmp_path));
ASSERT_TRUE(UidHasWriteAccessToPath(AID_ROOT, cache_path));
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index b473f26b2230..a3448fda60d9 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -269,7 +269,7 @@ TEST(IdmapTests, FabricatedOverlay) {
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "land-xxhdpi-v7")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "land")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "xxhdpi-v7")
- .SetResourceValue("drawable/dr1", fd, "port-xxhdpi-v7")
+ .SetResourceValue("drawable/dr1", fd, 0, 8341, "port-xxhdpi-v7")
.setFrroPath("/foo/bar/biz.frro")
.Build();
@@ -613,19 +613,19 @@ class TestVisitor : public Visitor {
}
void visit(const Idmap& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(Idmap)" << std::endl;
+ stream_ << "TestVisitor::visit(Idmap)" << '\n';
}
void visit(const IdmapHeader& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(IdmapHeader)" << std::endl;
+ stream_ << "TestVisitor::visit(IdmapHeader)" << '\n';
}
void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(IdmapData)" << std::endl;
+ stream_ << "TestVisitor::visit(IdmapData)" << '\n';
}
void visit(const IdmapData::Header& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
+ stream_ << "TestVisitor::visit(IdmapData::Header)" << '\n';
}
private:
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 380e462a3aba..40f98c2f351b 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -212,7 +212,7 @@ TEST(ResourceMappingTests, FabricatedOverlay) {
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
- .SetResourceValue("drawable/dr1", fd, "")
+ .SetResourceValue("drawable/dr1", fd, 0, 8341, "")
.setFrroPath("/foo/bar/biz.frro")
.Build();
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index c18b7efe44db..ee9c464219d9 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -206,21 +206,19 @@ int main(int argc, char** argv)
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t result = ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
- if (result != NO_ERROR) {
- close(fd);
- return 1;
- }
+ ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
ScreenCaptureResults captureResults = captureListener->waitForResults();
if (!captureResults.fenceResult.ok()) {
close(fd);
+ fprintf(stderr, "Failed to take screenshot. Status: %d\n",
+ fenceStatus(captureResults.fenceResult));
return 1;
}
ui::Dataspace dataspace = captureResults.capturedDataspace;
sp<GraphicBuffer> buffer = captureResults.buffer;
- result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
+ status_t result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
if (base == nullptr || result != NO_ERROR) {
String8 reason;
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 13c7946a033f..1488e14cfb8f 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -76,6 +76,8 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS =
"cleanup-orphan-phone-accounts";
private static final String COMMAND_RESET_CAR_MODE = "reset-car-mode";
+ private static final String COMMAND_IS_NON_IN_CALL_SERVICE_BOUND =
+ "is-non-ui-in-call-service-bound";
/**
* Change the system dialer package name if a package name was specified,
@@ -169,6 +171,8 @@ public final class Telecom extends BaseCommand {
+ " over Telephony.\n"
+ "telecom log-mark <MESSAGE>: emits a message into the telecom logs. Useful for "
+ "testers to indicate where in the logs various test steps take place.\n"
+ + "telecom is-non-ui-in-call-service-bound <PACKAGE>: queries a particular "
+ + "non-ui-InCallService in InCallController to determine if it is bound \n"
);
}
@@ -270,6 +274,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_GET_MAX_PHONES:
runGetMaxPhones();
break;
+ case COMMAND_IS_NON_IN_CALL_SERVICE_BOUND:
+ runIsNonUiInCallServiceBound();
+ break;
case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER:
runSetEmergencyPhoneAccountPackageFilter();
break;
@@ -428,6 +435,18 @@ public final class Telecom extends BaseCommand {
}
/**
+ * prints out whether a particular non-ui InCallServices is bound in a call
+ */
+ public void runIsNonUiInCallServiceBound() throws RemoteException {
+ if (TextUtils.isEmpty(mArgs.peekNextArg())) {
+ System.out.println("No Argument passed. Please pass a <PACKAGE_NAME> to lookup.");
+ } else {
+ System.out.println(
+ String.valueOf(mTelecomService.isNonUiInCallServiceBound(nextArg())));
+ }
+ }
+
+ /**
* Prints the mSIM config to the console.
* "DSDS" for a phone in DSDS mode
* "" (empty string) for a phone in SS mode
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
index 39248730802f..7489ce3eceea 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
@@ -8,6 +8,7 @@ import android.app.UiAutomation;
import android.app.UiAutomationConnection;
import android.content.Intent;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.RemoteException;
/**
@@ -26,6 +27,10 @@ public class UiAutomationShellWrapper {
throw new IllegalStateException("Already connected!");
}
mHandlerThread.start();
+ // The AccessibilityInteractionClient used by UiAutomation expects the main looper to
+ // be prepared. In most contexts this is normally done automatically, but must be called
+ // explicitly here because this is a shell tool.
+ Looper.prepareMainLooper();
mUiAutomation = new UiAutomation(mHandlerThread.getLooper(),
new UiAutomationConnection());
mUiAutomation.connect();
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index 4d41dcd3712d..e7361fe95e8e 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -1,87 +1,100 @@
# Usage
-## Two options to use the uinput command:
-### 1. Interactive through stdin:
-type `uinput -` into the terminal, then type/paste commands to send to the binary.
-Use Ctrl+D to signal end of stream to the binary (EOF).
-This mode can be also used from an app to send uinput events.
-For an example, see the cts test case at: [InputTestCase.java][2]
+There are two ways to use the `uinput` command:
-When using another program to control uinput in interactive mode, registering a
-new input device (for example, a bluetooth joystick) should be the first step.
-After the device is added, you need to wait for the _onInputDeviceAdded_
-(see [InputDeviceListener][1]) notification before issuing commands
-to the device.
-Failure to do so will cause missed events and inconsistent behavior.
+* **Recommended:** `uinput -` reads commands from standard input until End-of-File (Ctrl+D) is sent.
+ This mode can be used interactively from a terminal or used to control uinput from another program
+ or app (such as the CTS tests via [`UinputDevice`][UinputDevice]).
+* `uinput <filename>` reads commands from a file instead of standard input.
-### 2. Using a file as an input:
-type `uinput <filename>`, and the file will be used an an input to the binary.
-You must add a sufficient delay after a "register" command to ensure device
-is ready. The interactive mode is the recommended method of communicating
-with the uinput binary.
+[UinputDevice]: https://cs.android.com/android/platform/superproject/main/+/main:cts/libs/input/src/com/android/cts/input/UinputDevice.java
-All of the input commands should be in pseudo-JSON format as documented below.
-See examples [here][3].
+## Command format
-The file can have multiple commands one after the other (which is not strictly
-legal JSON format, as this would imply multiple root elements).
+Input commands should be in JSON format, though the parser is in [lenient mode] to allow comments,
+and integers can be specified in hexadecimal (e.g. `0xABCD`). The input file (or standard input) can
+contain multiple commands, which will be executed in sequence. Simply add multiple JSON objects to
+the file, one after the other without separators:
-## Command description
+```json5
+{
+ "id": 1,
+ "command": "register",
+ // ...
+}
+{
+ "id": 1,
+ "command": "delay",
+ // ...
+}
+```
+
+Many examples of command files can be found [in the CTS tests][cts-example-jsons].
+
+[lenient mode]: https://developer.android.com/reference/android/util/JsonReader#setLenient(boolean)
+[cts-example-jsons]: https://cs.android.com/android/platform/superproject/main/+/main:cts/tests/tests/hardware/res/raw/
+
+## Command reference
+
+### `register`
-1. `register`
Register a new uinput device
-| Field | Type | Description |
-|:-------------:|:-------------:|:-------------------------- |
-| id | integer | Device id |
-| command | string | Must be set to "register" |
-| name | string | Device name |
-| vid | 16-bit integer| Vendor id |
-| pid | 16-bit integer| Product id |
-| bus | string | Bus that device should use |
-| configuration | int array | uinput device configuration|
-| ff_effects_max| integer | ff_effects_max value |
-| abs_info | array | ABS axes information |
-
-Device ID is used for matching the subsequent commands to a specific device
-to avoid ambiguity when multiple devices are registered.
-
-Device bus is used to determine how the uinput device is connected to the host.
-The options are "usb" and "bluetooth".
-
-Device configuration is used to configure uinput device. "type" field provides the UI_SET_*
-control code, and data is a vector of control values to be sent to uinput device, depends on
-the control code.
+| Field | Type | Description |
+|:----------------:|:--------------:|:-------------------------- |
+| `id` | integer | Device ID |
+| `command` | string | Must be set to "register" |
+| `name` | string | Device name |
+| `vid` | 16-bit integer | Vendor ID |
+| `pid` | 16-bit integer | Product ID |
+| `bus` | string | Bus that device should use |
+| `port` | string | `phys` value to report |
+| `configuration` | object array | uinput device configuration|
+| `ff_effects_max` | integer | `ff_effects_max` value |
+| `abs_info` | array | Absolute axes information |
-| Field | Type | Description |
-|:-------------:|:-------------:|:-------------------------- |
-| type | integer | UI_SET_ control type |
-| data | int array | control values |
+`id` is used for matching the subsequent commands to a specific device to avoid ambiguity when
+multiple devices are registered.
-Device ff_effects_max must be provided if FFBIT is set.
+`bus` is used to determine how the uinput device is connected to the host. The options are `"usb"`
+and `"bluetooth"`.
-Device abs_info fields are provided to set the device axes information. It is an array of below
-objects:
-| Field | Type | Description |
-|:-------------:|:-------------:|:-------------------------- |
-| code | integer | Axis code |
-| info | object | ABS information object |
+Device configuration is used to configure the uinput device. The `type` field provides a `UI_SET_*`
+control code as an integer value or a string label (e.g. `"UI_SET_EVBIT"`), and data is a vector of
+control values to be sent to the uinput device, which depends on the control code.
+
+| Field | Type | Description |
+|:-------------:|:---------------------:|:-----------------------|
+| `type` | integer\|string | `UI_SET_` control type |
+| `data` | integer\|string array | control values |
+
+Due to the sequential nature in which this is parsed, the `type` field must be specified before
+the `data` field in this JSON Object.
+
+`ff_effects_max` must be provided if `UI_SET_FFBIT` is used in `configuration`.
+
+`abs_info` fields are provided to set the device axes information. It is an array of below objects:
+
+| Field | Type | Description |
+|:-------------:|:---------------:|:------------------------|
+| `code` | integer\|string | Axis code or label |
+| `info` | object | Axis information object |
+
+The axis information object is defined as below, with the fields having the same meaning as those
+Linux's [`struct input_absinfo`][struct input_absinfo]:
-ABS information object is defined as below:
| Field | Type | Description |
|:-------------:|:-------------:|:-------------------------- |
-| value | integer | Latest reported value |
-| minimum | integer | Minimum value for the axis |
-| maximum | integer | Maximum value for the axis |
-| fuzz | integer | fuzz value for noise filter|
-| flat | integer | values to be discarded |
-| resolution | integer | resolution of axis |
-
-See [struct input_absinfo][4]) definitions.
+| `value` | integer | Latest reported value |
+| `minimum` | integer | Minimum value for the axis |
+| `maximum` | integer | Maximum value for the axis |
+| `fuzz` | integer | fuzz value for noise filter|
+| `flat` | integer | values to be discarded |
+| `resolution` | integer | resolution of axis |
Example:
-```json
+```json5
{
"id": 1,
"command": "register",
@@ -90,33 +103,56 @@ Example:
"pid": 0x2c42,
"bus": "usb",
"configuration":[
- {"type":100, "data":[1, 21]}, // UI_SET_EVBIT : EV_KEY and EV_FF
- {"type":101, "data":[11, 2, 3, 4]}, // UI_SET_KEYBIT : KEY_0 KEY_1 KEY_2 KEY_3
- {"type":107, "data":[80]} // UI_SET_FFBIT : FF_RUMBLE
+ {"type":"UI_SET_EVBIT", "data":["EV_KEY", "EV_FF"]},
+ {"type":"UI_SET_KEYBIT", "data":["KEY_0", "KEY_1", "KEY_2", "KEY_3"]},
+ {"type":"UI_SET_ABSBIT", "data":["ABS_Y", "ABS_WHEEL"]},
+ {"type":"UI_SET_FFBIT", "data":["FF_RUMBLE"]}
],
"ff_effects_max" : 1,
"abs_info": [
- {"code":1, "info": {"value":20, "minimum":-255,
+ {"code":"ABS_Y", "info": {"value":20, "minimum":-255,
"maximum":255, "fuzz":0, "flat":0, "resolution":1}
},
- {"code":8, "info": {"value":-50, "minimum":-255,
+ {"code":"ABS_WHEEL", "info": {"value":-50, "minimum":-255,
"maximum":255, "fuzz":0, "flat":0, "resolution":1}
}
]
}
-
```
-2. `delay`
+
+[struct input_absinfo]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/kernel/uapi/linux/input.h?q=%22struct%20input_absinfo%22
+
+#### Waiting for registration
+
+After the command is sent, there will be a delay before the device is set up by the Android input
+stack, and `uinput` does not wait for that process to finish. Any commands sent to the device during
+that time will be dropped. If you are controlling `uinput` by sending commands through standard
+input from an app, you need to wait for [`onInputDeviceAdded`][onInputDeviceAdded] to be called on
+an `InputDeviceListener` before issuing commands to the device. If you are passing a file to
+`uinput`, add a `delay` after the `register` command to let registration complete. You can add a
+`sync` in certain positions, like at the end of the file to get a response when all commands have
+finished processing.
+
+[onInputDeviceAdded]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html
+
+#### Unregistering the device
+
+As soon as EOF is reached (either in interactive mode, or in file mode), the device that was created
+will be unregistered. There is no explicit command for unregistering a device.
+
+### `delay`
+
Add a delay to command processing
| Field | Type | Description |
|:-------------:|:-------------:|:-------------------------- |
-| id | integer | Device id |
-| command | string | Must be set to "delay" |
-| duration | integer | Delay in milliseconds |
+| `id` | integer | Device ID |
+| `command` | string | Must be set to "delay" |
+| `duration` | integer | Delay in milliseconds |
Example:
-```json
+
+```json5
{
"id": 1,
"command": "delay",
@@ -124,36 +160,37 @@ Example:
}
```
-3. `inject`
-Send an array of uinput event packets [type, code, value] to the uinput device
+### `inject`
-| Field | Type | Description |
-|:-------------:|:-------------:|:-------------------------- |
-| id | integer | Device id |
-| command | string | Must be set to "inject" |
-| events | integer array | events to inject |
+Send an array of uinput event packets to the uinput device
-The "events" parameter is an array of integers, encapsulates evdev input_event type, code and value,
-see the example below.
+| Field | Type | Description |
+|:-------------:|:---------------------:|:-------------------------- |
+| `id` | integer | Device ID |
+| `command` | string | Must be set to "inject" |
+| `events` | integer\|string array | events to inject |
-Example:
-```json
+The `events` parameter is an array of integers in sets of three: a type, an axis code, and an axis
+value, like you'd find in Linux's `struct input_event`. For example, sending presses of the 0 and 1
+keys would look like this:
+
+```json5
{
"id": 1,
"command": "inject",
- "events": [0x01, 0xb, 0x1, // EV_KEY, KEY_0, DOWN
- 0x00, 0x00, 0x00, // EV_SYN, SYN_REPORT, 0
- 0x01, 0x0b, 0x00, // EV_KEY, KEY_0, UP
- 0x00, 0x00, 0x00, // EV_SYN, SYN_REPORT, 0
- 0x01, 0x2, 0x1, // EV_KEY, KEY_1, DOWN
- 0x00, 0x00, 0x01, // EV_SYN, SYN_REPORT, 0
- 0x01, 0x02, 0x00, // EV_KEY, KEY_1, UP
- 0x00, 0x00, 0x01 // EV_SYN, SYN_REPORT, 0
+ "events": ["EV_KEY", "KEY_0", 1,
+ "EV_SYN", "SYN_REPORT", 0,
+ "EV_KEY", "KEY_0", 0,
+ "EV_SYN", "SYN_REPORT", 0,
+ "EV_KEY", "KEY_1", 1,
+ "EV_SYN", "SYN_REPORT", 0,
+ "EV_KEY", "KEY_1", 0,
+ "EV_SYN", "SYN_REPORT", 0
]
}
```
-4. `sync`
+### `sync`
A command used to get a response once the command is processed. When several `inject` and `delay`
commands are used in a row, the `sync` command can be used to track the progress of the command
@@ -185,14 +222,6 @@ This command will result in the following response when it is processed:
}
```
-### Notes
-1. As soon as EOF is reached (either in interactive mode, or in file mode),
-the device that was created will be unregistered. There is no
-explicit command for unregistering a device.
-2. The `getevent` utility can used to print out the key events
-for debugging purposes.
-
-[1]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html
-[2]: ../../../../cts/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
-[3]: ../../../../cts/tests/tests/hardware/res/raw/
-[4]: ../../../../bionic/libc/kernel/uapi/linux/input.h
+## Notes
+
+The `getevent` utility can used to print out the key events for debugging purposes.
diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp
index c56adc35b580..558bcc54c7ef 100644
--- a/cmds/uinput/jni/Android.bp
+++ b/cmds/uinput/jni/Android.bp
@@ -21,6 +21,7 @@ cc_library_shared {
"libbase",
"libbinder",
"liblog",
+ "libinput",
"libnativehelper",
],
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
index 3f4163dc54ec..7659054119c8 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -16,12 +16,24 @@
#define LOG_TAG "UinputCommandDevice"
-#include <linux/uinput.h>
+#include "com_android_commands_uinput_Device.h"
+#include <android-base/stringprintf.h>
+#include <android/looper.h>
+#include <android_os_Parcel.h>
#include <fcntl.h>
+#include <input/InputEventLabels.h>
#include <inttypes.h>
+#include <jni.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
#include <time.h>
#include <unistd.h>
+
#include <algorithm>
#include <array>
#include <cstdio>
@@ -30,19 +42,6 @@
#include <memory>
#include <vector>
-#include <android/looper.h>
-#include <android_os_Parcel.h>
-#include <jni.h>
-#include <log/log.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
-
-#include <android-base/stringprintf.h>
-
-#include "com_android_commands_uinput_Device.h"
-
namespace android {
namespace uinput {
@@ -307,6 +306,21 @@ static void setAbsInfo(JNIEnv* env, jclass /* clazz */, jint handle, jint axisCo
::ioctl(static_cast<int>(handle), UI_ABS_SETUP, &absSetup);
}
+static jint getEvdevEventTypeByLabel(JNIEnv* env, jclass /* clazz */, jstring rawLabel) {
+ ScopedUtfChars label(env, rawLabel);
+ return InputEventLookup::getLinuxEvdevEventTypeByLabel(label.c_str()).value_or(-1);
+}
+
+static jint getEvdevEventCodeByLabel(JNIEnv* env, jclass /* clazz */, jint type, jstring rawLabel) {
+ ScopedUtfChars label(env, rawLabel);
+ return InputEventLookup::getLinuxEvdevEventCodeByLabel(type, label.c_str()).value_or(-1);
+}
+
+static jint getEvdevInputPropByLabel(JNIEnv* env, jclass /* clazz */, jstring rawLabel) {
+ ScopedUtfChars label(env, rawLabel);
+ return InputEventLookup::getLinuxEvdevInputPropByLabel(label.c_str()).value_or(-1);
+}
+
static JNINativeMethod sMethods[] = {
{"nativeOpenUinputDevice",
"(Ljava/lang/String;IIIIILjava/lang/String;"
@@ -316,6 +330,12 @@ static JNINativeMethod sMethods[] = {
{"nativeConfigure", "(II[I)V", reinterpret_cast<void*>(configure)},
{"nativeSetAbsInfo", "(IILandroid/os/Parcel;)V", reinterpret_cast<void*>(setAbsInfo)},
{"nativeCloseUinputDevice", "(J)V", reinterpret_cast<void*>(closeUinputDevice)},
+ {"nativeGetEvdevEventTypeByLabel", "(Ljava/lang/String;)I",
+ reinterpret_cast<void*>(getEvdevEventTypeByLabel)},
+ {"nativeGetEvdevEventCodeByLabel", "(ILjava/lang/String;)I",
+ reinterpret_cast<void*>(getEvdevEventCodeByLabel)},
+ {"nativeGetEvdevInputPropByLabel", "(Ljava/lang/String;)I",
+ reinterpret_cast<void*>(getEvdevInputPropByLabel)},
};
int register_com_android_commands_uinput_Device(JNIEnv* env) {
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index 2f362d7fabab..ad5e70f4ff0c 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -67,6 +67,9 @@ public class Device {
private static native void nativeInjectEvent(long ptr, int type, int code, int value);
private static native void nativeConfigure(int handle, int code, int[] configs);
private static native void nativeSetAbsInfo(int handle, int axisCode, Parcel axisParcel);
+ private static native int nativeGetEvdevEventTypeByLabel(String label);
+ private static native int nativeGetEvdevEventCodeByLabel(int type, String label);
+ private static native int nativeGetEvdevInputPropByLabel(String label);
public Device(int id, String name, int vid, int pid, int bus,
SparseArray<int[]> configuration, int ffEffectsMax,
@@ -264,4 +267,32 @@ public class Device {
throw new RuntimeException(e);
}
}
+
+ static int getEvdevEventTypeByLabel(String label) {
+ final var type = nativeGetEvdevEventTypeByLabel(label);
+ if (type < 0) {
+ throw new IllegalArgumentException(
+ "Failed to get evdev event type from label: " + label);
+ }
+ return type;
+ }
+
+ static int getEvdevEventCodeByLabel(int type, String label) {
+ final var code = nativeGetEvdevEventCodeByLabel(type, label);
+ if (code < 0) {
+ throw new IllegalArgumentException(
+ "Failed to get evdev event code for type " + type + " from label: " + label);
+ }
+ return code;
+
+ }
+
+ static int getEvdevInputPropByLabel(String label) {
+ final var prop = nativeGetEvdevInputPropByLabel(label);
+ if (prop < 0) {
+ throw new IllegalArgumentException(
+ "Failed to get evdev input prop from label: " + label);
+ }
+ return prop;
+ }
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 91b4a7418c42..9d8f1f400950 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -25,7 +25,9 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
+import java.util.function.Function;
import java.util.stream.IntStream;
import src.com.android.commands.uinput.InputAbsInfo;
@@ -37,8 +39,6 @@ import src.com.android.commands.uinput.InputAbsInfo;
public class Event {
private static final String TAG = "UinputEvent";
- private static final int ABS_CNT = 64;
-
enum Command {
REGISTER("register"),
DELAY("delay"),
@@ -52,6 +52,36 @@ public class Event {
}
}
+ private static final int EV_KEY = 0x01;
+ private static final int EV_REL = 0x02;
+ private static final int EV_ABS = 0x03;
+ private static final int EV_MSC = 0x04;
+ private static final int EV_SW = 0x05;
+ private static final int EV_LED = 0x11;
+ private static final int EV_SND = 0x12;
+ private static final int EV_FF = 0x15;
+
+ private enum UinputControlCode {
+ UI_SET_EVBIT("UI_SET_EVBIT", 100),
+ UI_SET_KEYBIT("UI_SET_KEYBIT", 101),
+ UI_SET_RELBIT("UI_SET_RELBIT", 102),
+ UI_SET_ABSBIT("UI_SET_ABSBIT", 103),
+ UI_SET_MSCBIT("UI_SET_MSCBIT", 104),
+ UI_SET_LEDBIT("UI_SET_LEDBIT", 105),
+ UI_SET_SNDBIT("UI_SET_SNDBIT", 106),
+ UI_SET_FFBIT("UI_SET_FFBIT", 107),
+ UI_SET_SWBIT("UI_SET_SWBIT", 109),
+ UI_SET_PROPBIT("UI_SET_PROPBIT", 110);
+
+ final String mName;
+ final int mValue;
+
+ UinputControlCode(String name, int value) {
+ this.mName = name;
+ this.mValue = value;
+ }
+ }
+
// These constants come from "include/uapi/linux/input.h" in the kernel
enum Bus {
USB(0x03), BLUETOOTH(0x05);
@@ -291,8 +321,8 @@ public class Event {
eb.setBus(readBus());
break;
case "events":
- int[] injections = readIntList().stream()
- .mapToInt(Integer::intValue).toArray();
+ int[] injections = readInjectedEvents().stream()
+ .mapToInt(Integer::intValue).toArray();
eb.setInjections(injections);
break;
case "configuration":
@@ -330,12 +360,17 @@ public class Event {
return e;
}
- private ArrayList<Integer> readIntList() throws IOException {
- ArrayList<Integer> data = new ArrayList<Integer>();
+ private ArrayList<Integer> readInjectedEvents() throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
try {
mReader.beginArray();
while (mReader.hasNext()) {
- data.add(Integer.decode(mReader.nextString()));
+ // Read events in groups of three, because we expect an event type, event code,
+ // and event value.
+ final int type = readEvdevEventType();
+ data.add(type);
+ data.add(readEvdevEventCode(type));
+ data.add(readInt());
}
mReader.endArray();
} catch (IllegalStateException | NumberFormatException e) {
@@ -346,22 +381,32 @@ public class Event {
return data;
}
- private byte[] readData() throws IOException {
- ArrayList<Integer> data = readIntList();
- byte[] rawData = new byte[data.size()];
- for (int i = 0; i < data.size(); i++) {
- int d = data.get(i);
- if ((d & 0xFF) != d) {
- throw new IllegalStateException("Invalid data, all values must be byte-sized");
+ private int readValueAsInt(Function<String, Integer> stringToInt) throws IOException {
+ switch (mReader.peek()) {
+ case NUMBER: {
+ return mReader.nextInt();
+ }
+ case STRING: {
+ final var str = mReader.nextString();
+ try {
+ // Attempt to first parse the value as an int.
+ return Integer.decode(str);
+ } catch (NumberFormatException e) {
+ // Then fall back to the supplied function.
+ return stringToInt.apply(str);
+ }
+ }
+ default: {
+ throw new IllegalStateException(
+ "Encountered malformed data. Expected int or string.");
}
- rawData[i] = (byte) d;
}
- return rawData;
}
private int readInt() throws IOException {
- String val = mReader.nextString();
- return Integer.decode(val);
+ return readValueAsInt((str) -> {
+ throw new IllegalStateException("Encountered malformed data. Expected int.");
+ });
}
private Bus readBus() throws IOException {
@@ -375,17 +420,20 @@ public class Event {
try {
mReader.beginArray();
while (mReader.hasNext()) {
- int type = 0;
+ UinputControlCode controlCode = null;
IntStream data = null;
mReader.beginObject();
while (mReader.hasNext()) {
String name = mReader.nextName();
switch (name) {
case "type":
- type = readInt();
+ controlCode = readUinputControlCode();
break;
case "data":
- data = readIntList().stream().mapToInt(Integer::intValue);
+ Objects.requireNonNull(controlCode,
+ "Configuration 'type' must be specified before 'data'.");
+ data = readDataForControlCode(controlCode)
+ .stream().mapToInt(Integer::intValue);
break;
default:
consumeRemainingElements();
@@ -395,9 +443,9 @@ public class Event {
}
}
mReader.endObject();
- if (data != null) {
- final int[] existing = configuration.get(type);
- configuration.put(type, existing == null ? data.toArray()
+ if (controlCode != null && data != null) {
+ final int[] existing = configuration.get(controlCode.mValue);
+ configuration.put(controlCode.mValue, existing == null ? data.toArray()
: IntStream.concat(IntStream.of(existing), data).toArray());
}
}
@@ -410,6 +458,60 @@ public class Event {
return configuration;
}
+ private UinputControlCode readUinputControlCode() throws IOException {
+ var code = readValueAsInt((controlTypeStr) -> {
+ for (UinputControlCode controlCode : UinputControlCode.values()) {
+ if (controlCode.mName.equals(controlTypeStr)) {
+ return controlCode.mValue;
+ }
+ }
+ return -1;
+ });
+ for (UinputControlCode controlCode : UinputControlCode.values()) {
+ if (controlCode.mValue == code) {
+ return controlCode;
+ }
+ }
+ return null;
+ }
+
+ private List<Integer> readDataForControlCode(
+ UinputControlCode controlCode) throws IOException {
+ return switch (controlCode) {
+ case UI_SET_EVBIT -> readArrayAsInts(this::readEvdevEventType);
+ case UI_SET_KEYBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_KEY));
+ case UI_SET_RELBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_REL));
+ case UI_SET_ABSBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_ABS));
+ case UI_SET_MSCBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_MSC));
+ case UI_SET_LEDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_LED));
+ case UI_SET_SNDBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SND));
+ case UI_SET_FFBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_FF));
+ case UI_SET_SWBIT -> readArrayAsInts(() -> readEvdevEventCode(EV_SW));
+ case UI_SET_PROPBIT -> readArrayAsInts(this::readEvdevInputProp);
+ };
+ }
+
+ interface IntValueReader {
+ int readNextValue() throws IOException;
+ }
+
+ private ArrayList<Integer> readArrayAsInts(
+ IntValueReader nextValueReader) throws IOException {
+ ArrayList<Integer> data = new ArrayList<>();
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ data.add(nextValueReader.readNextValue());
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return data;
+ }
+
private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
InputAbsInfo absInfo = new InputAbsInfo();
try {
@@ -463,7 +565,7 @@ public class Event {
String name = mReader.nextName();
switch (name) {
case "code":
- type = readInt();
+ type = readEvdevEventCode(EV_ABS);
break;
case "info":
absInfo = readAbsInfo();
@@ -489,6 +591,18 @@ public class Event {
return infoArray;
}
+ private int readEvdevEventType() throws IOException {
+ return readValueAsInt(Device::getEvdevEventTypeByLabel);
+ }
+
+ private int readEvdevEventCode(int type) throws IOException {
+ return readValueAsInt((str) -> Device.getEvdevEventCodeByLabel(type, str));
+ }
+
+ private int readEvdevInputProp() throws IOException {
+ return readValueAsInt(Device::getEvdevInputPropByLabel);
+ }
+
private void consumeRemainingElements() throws IOException {
while (mReader.hasNext()) {
mReader.skipValue();