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.cpp33
-rw-r--r--cmds/idmap2/idmap2/Main.cpp8
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp46
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.h15
-rw-r--r--cmds/idmap2/include/idmap2/BinaryStreamVisitor.h2
-rw-r--r--cmds/idmap2/include/idmap2/FabricatedOverlay.h3
-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/XmlParser.h2
-rw-r--r--cmds/idmap2/libidmap2/CommandLineOptions.cpp8
-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/ResourceContainer.cpp4
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp2
-rw-r--r--cmds/idmap2/tests/FileUtilsTests.cpp5
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp8
-rw-r--r--cmds/idmap2/tests/ResourceUtilsTests.cpp4
-rw-r--r--cmds/incidentd/src/IncidentService.cpp20
-rw-r--r--cmds/locksettings/Android.bp5
-rwxr-xr-xcmds/locksettings/locksettings.sh6
-rw-r--r--cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java47
-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.md238
-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.java163
40 files changed, 607 insertions, 326 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 a8b6c0b70804..10ce3b8d8161 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 <vector>
@@ -28,6 +29,7 @@
#include <math.h>
#include <fcntl.h>
#include <utils/misc.h>
+#include <utils/Trace.h>
#include <signal.h>
#include <time.h>
@@ -79,18 +81,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 = ' ';
@@ -200,6 +202,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", "");
@@ -213,6 +216,7 @@ BootAnimation::BootAnimation(sp<Callbacks> callbacks)
}
BootAnimation::~BootAnimation() {
+ ATRACE_CALL();
if (mAnimation != nullptr) {
releaseAnimation(mAnimation);
mAnimation = nullptr;
@@ -222,6 +226,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) {
@@ -240,6 +245,7 @@ sp<SurfaceComposerClient> BootAnimation::session() const {
}
void BootAnimation::binderDied(const wp<IBinder>&) {
+ ATRACE_CALL();
// woah, surfaceflinger died!
SLOGD("SurfaceFlinger died, exiting...");
@@ -251,6 +257,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) {
@@ -282,6 +289,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;
@@ -338,6 +346,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);
@@ -404,10 +413,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);
@@ -492,6 +503,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();
@@ -628,6 +640,7 @@ status_t BootAnimation::readyToRun() {
}
void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() {
+ ATRACE_CALL();
const auto orientation = parseOrientationProperty();
if (orientation == ui::ROTATION_0) {
@@ -650,6 +663,7 @@ void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() {
}
ui::Rotation BootAnimation::parseOrientationProperty() {
+ ATRACE_CALL();
const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
if (displayIds.size() == 0) {
return ui::ROTATION_0;
@@ -672,11 +686,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;
@@ -704,6 +720,7 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
}
bool BootAnimation::preloadAnimation() {
+ ATRACE_CALL();
findBootAnimationFile();
if (!mZipFileName.isEmpty()) {
mAnimation = loadAnimation(mZipFileName);
@@ -714,6 +731,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();
@@ -724,6 +742,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,
@@ -747,6 +766,7 @@ void BootAnimation::findBootAnimationFile() {
}
GLuint compileShader(GLenum shaderType, const GLchar *source) {
+ ATRACE_CALL();
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &source, 0);
glCompileShader(shader);
@@ -765,6 +785,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);
@@ -780,6 +801,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 =
@@ -813,6 +835,7 @@ void BootAnimation::initShaders() {
}
bool BootAnimation::threadLoop() {
+ ATRACE_CALL();
bool result;
initShaders();
@@ -838,6 +861,7 @@ bool BootAnimation::threadLoop() {
}
bool BootAnimation::android() {
+ ATRACE_CALL();
glActiveTexture(GL_TEXTURE0);
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
@@ -905,6 +929,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");
@@ -915,10 +940,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;
@@ -935,6 +962,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
@@ -963,6 +991,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;
@@ -985,6 +1014,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;
@@ -996,6 +1026,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) {
@@ -1018,6 +1049,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) {
@@ -1045,6 +1077,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);
@@ -1092,6 +1125,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;
@@ -1117,6 +1151,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];
@@ -1129,6 +1164,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)) {
@@ -1252,6 +1288,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;
@@ -1357,6 +1394,7 @@ bool BootAnimation::preloadZip(Animation& animation) {
}
bool BootAnimation::movie() {
+ ATRACE_CALL();
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
@@ -1450,6 +1488,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);
@@ -1461,6 +1500,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);
@@ -1484,6 +1524,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, "");
@@ -1510,6 +1551,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;
@@ -1720,12 +1762,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;
}
@@ -1768,6 +1812,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)
@@ -1779,6 +1824,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.string());
@@ -1810,6 +1856,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
@@ -1853,11 +1900,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) {
@@ -1868,6 +1917,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.
@@ -1905,6 +1955,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) {
@@ -1915,6 +1966,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 f41e57cc66d6..3704b4aff6c7 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>
@@ -174,7 +174,7 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
return Error("failed to parse config");
}
- std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
+ std::vector<AssetManager2::ApkAssetsPtr> apk_assets;
std::string target_path;
std::string target_package_name;
for (size_t i = 0; i < idmap_paths.size(); i++) {
@@ -217,24 +217,21 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
apk_assets.push_back(std::move(overlay_apk));
}
- // AssetManager2::SetApkAssets requires raw ApkAssets pointers, not unique_ptrs
- std::vector<const ApkAssets*> raw_pointer_apk_assets;
- std::transform(apk_assets.cbegin(), apk_assets.cend(), std::back_inserter(raw_pointer_apk_assets),
- [](const auto& p) -> const ApkAssets* { return p.get(); });
- AssetManager2 am;
- am.SetApkAssets(raw_pointer_apk_assets);
- am.SetConfiguration(config);
-
- const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
- if (!resid) {
- return Error(resid.GetError(), "failed to parse resource ID");
- }
+ {
+ // Make sure |apk_assets| vector outlives the asset manager as it doesn't own the assets.
+ AssetManager2 am(apk_assets, config);
+
+ const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
+ if (!resid) {
+ return Error(resid.GetError(), "failed to parse resource ID");
+ }
- const Result<std::string> value = GetValue(&am, *resid);
- if (!value) {
- return Error(value.GetError(), "resource 0x%08x not found", *resid);
+ const Result<std::string> value = GetValue(&am, *resid);
+ if (!value) {
+ return Error(value.GetError(), "resource 0x%08x not found", *resid);
+ }
+ std::cout << *value << '\n';
}
- std::cout << *value << std::endl;
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..b94b3b458065 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(
@@ -257,7 +281,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/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..a29fa8f3e1ab 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>
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/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 8129d99650f7..888b3a5b129d 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -19,8 +19,8 @@
#include <algorithm>
#include <cassert>
#include <iomanip>
-#include <iostream>
#include <memory>
+#include <ostream>
#include <set>
#include <sstream>
#include <string>
@@ -131,7 +131,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());
}
@@ -168,7 +168,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) {
@@ -181,7 +181,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/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 3531cd7c2f36..174d85c71828 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/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index 0e3590486c6f..7869fbdb8cea 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -262,7 +262,7 @@ OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am,
}
struct ResState {
- std::unique_ptr<ApkAssets> apk_assets;
+ AssetManager2::ApkAssetsPtr apk_assets;
const LoadedArsc* arsc;
const LoadedPackage* package;
std::unique_ptr<AssetManager2> am;
@@ -284,7 +284,7 @@ struct ResState {
}
state.am = std::make_unique<AssetManager2>();
- if (!state.am->SetApkAssets({state.apk_assets.get()})) {
+ if (!state.am->SetApkAssets({state.apk_assets})) {
return Error("failed to create asset manager");
}
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 70822c890288..766ca565a23c 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>
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..f6e48ba7a1f4 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -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/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 69142086765c..011040ba0ebf 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -38,7 +38,7 @@ class ResourceUtilsTests : public Idmap2Tests {
apk_assets_ = ApkAssets::Load(GetTargetApkPath());
ASSERT_THAT(apk_assets_, NotNull());
- am_.SetApkAssets({apk_assets_.get()});
+ am_.SetApkAssets({apk_assets_});
}
const AssetManager2& GetAssetManager() {
@@ -47,7 +47,7 @@ class ResourceUtilsTests : public Idmap2Tests {
private:
AssetManager2 am_;
- std::unique_ptr<const ApkAssets> apk_assets_;
+ AssetManager2::ApkAssetsPtr apk_assets_;
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 05a43ad7d936..4f9059f02281 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -502,9 +502,13 @@ status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel*
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
+ unique_fd in, out, err;
+ if (status_t status = data.readUniqueFileDescriptor(&in); status != OK) return status;
+
+ if (status_t status = data.readUniqueFileDescriptor(&out); status != OK) return status;
+
+ if (status_t status = data.readUniqueFileDescriptor(&err); status != OK) return status;
+
int argc = data.readInt32();
Vector<String8> args;
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
@@ -517,15 +521,15 @@ status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel*
return BAD_VALUE;
}
- FILE* fin = fdopen(in, "r");
- FILE* fout = fdopen(out, "w");
- FILE* ferr = fdopen(err, "w");
+ FILE* fin = fdopen(in.release(), "r");
+ FILE* fout = fdopen(out.release(), "w");
+ FILE* ferr = fdopen(err.release(), "w");
if (fin == NULL || fout == NULL || ferr == NULL) {
resultReceiver->send(NO_MEMORY);
} else {
- err = command(fin, fout, ferr, args);
- resultReceiver->send(err);
+ status_t result = command(fin, fout, ferr, args);
+ resultReceiver->send(result);
}
if (fin != NULL) {
diff --git a/cmds/locksettings/Android.bp b/cmds/locksettings/Android.bp
index 5ee582450361..ee31aed14385 100644
--- a/cmds/locksettings/Android.bp
+++ b/cmds/locksettings/Android.bp
@@ -21,8 +21,7 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-java_binary {
+sh_binary {
name: "locksettings",
- wrapper: "locksettings.sh",
- srcs: ["**/*.java"],
+ src: "locksettings.sh",
}
diff --git a/cmds/locksettings/locksettings.sh b/cmds/locksettings/locksettings.sh
index 0ef4fa9f6771..2f8d86844eb1 100755
--- a/cmds/locksettings/locksettings.sh
+++ b/cmds/locksettings/locksettings.sh
@@ -1,6 +1,2 @@
#!/system/bin/sh
-# Script to start "locksettings" on the device
-#
-base=/system
-export CLASSPATH=$base/framework/locksettings.jar
-exec app_process $base/bin com.android.commands.locksettings.LockSettingsCmd "$@"
+cmd lock_settings "$@"
diff --git a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
deleted file mode 100644
index 7d9260a77158..000000000000
--- a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.commands.locksettings;
-
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.ShellCallback;
-
-import com.android.internal.os.BaseCommand;
-import com.android.internal.widget.ILockSettings;
-
-import java.io.FileDescriptor;
-import java.io.PrintStream;
-
-public final class LockSettingsCmd extends BaseCommand {
-
- public static void main(String[] args) {
- (new LockSettingsCmd()).run(args);
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- main(new String[] { "help" });
- }
-
- @Override
- public void onRun() throws Exception {
- ILockSettings lockSettings = ILockSettings.Stub.asInterface(
- ServiceManager.getService("lock_settings"));
- lockSettings.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out,
- FileDescriptor.err, getRawArgs(), new ShellCallback(), new ResultReceiver(null) {});
- }
-}
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 47e1dad9ccd6..bdec8b97bae6 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -1,87 +1,99 @@
# 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 |
+| `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 +102,54 @@ 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.
+
+[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,43 +157,36 @@ 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
]
}
```
-### 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 732b33d60c15..6458eef99008 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -66,6 +66,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,
@@ -234,4 +237,32 @@ public class Device {
msg.sendToTarget();
}
}
+
+ 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 4b090f5a713c..cddb407475a2 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -25,6 +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;
@@ -39,7 +42,35 @@ public class Event {
public static final String COMMAND_REGISTER = "register";
public static final String COMMAND_DELAY = "delay";
public static final String COMMAND_INJECT = "inject";
- private static final int ABS_CNT = 64;
+ 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 {
@@ -257,8 +288,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":
@@ -293,12 +324,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) {
@@ -309,22 +345,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 {
@@ -338,17 +384,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();
@@ -358,9 +407,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());
}
}
@@ -373,6 +422,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 {
@@ -426,7 +529,7 @@ public class Event {
String name = mReader.nextName();
switch (name) {
case "code":
- type = readInt();
+ type = readEvdevEventCode(EV_ABS);
break;
case "info":
absInfo = readAbsInfo();
@@ -452,6 +555,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();