diff options
Diffstat (limited to 'cmds')
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 = ∂ @@ -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(); |