diff options
Diffstat (limited to 'cmds')
| -rw-r--r-- | cmds/bootanimation/Android.mk | 48 | ||||
| -rw-r--r-- | cmds/bootanimation/BootAnimation.cpp | 95 | ||||
| -rw-r--r-- | cmds/bootanimation/BootAnimation.h | 86 | ||||
| -rw-r--r-- | cmds/bootanimation/bootanimation_main.cpp | 106 | ||||
| -rw-r--r-- | cmds/hid/README.md | 145 | ||||
| -rw-r--r-- | cmds/hid/jni/Android.mk | 9 | ||||
| -rw-r--r-- | cmds/hid/jni/com_android_commands_hid_Device.cpp | 125 | ||||
| -rw-r--r-- | cmds/hid/jni/com_android_commands_hid_Device.h | 9 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Device.java | 26 | ||||
| -rw-r--r-- | cmds/hid/src/com/android/commands/hid/Hid.java | 13 | ||||
| -rw-r--r-- | cmds/pm/src/com/android/commands/pm/Pm.java | 2 | ||||
| -rw-r--r-- | cmds/screencap/screencap.cpp | 39 | ||||
| -rw-r--r-- | cmds/uiautomator/instrumentation/Android.mk | 1 | ||||
| -rw-r--r-- | cmds/uiautomator/library/Android.mk | 5 |
14 files changed, 485 insertions, 224 deletions
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 0e2c13ee1719..7ab402a2cd46 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -1,14 +1,49 @@ +bootanimation_CommonCFlags = -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +bootanimation_CommonCFlags += -Wall -Werror -Wunused -Wunreachable-code + + +# bootanimation executable +# ========================================================= + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ bootanimation_main.cpp \ audioplay.cpp \ - BootAnimation.cpp -LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS += ${bootanimation_CommonCFlags} + +LOCAL_SHARED_LIBRARIES := \ + libOpenSLES \ + libandroidfw \ + libbase \ + libbinder \ + libbootanimation \ + libcutils \ + liblog \ + libutils \ + +LOCAL_MODULE:= bootanimation + +LOCAL_INIT_RC := bootanim.rc + +ifdef TARGET_32_BIT_SURFACEFLINGER +LOCAL_32_BIT_ONLY := true +endif + +include $(BUILD_EXECUTABLE) -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code + +# libbootanimation +# =========================================================== + +include $(CLEAR_VARS) +LOCAL_MODULE := libbootanimation +LOCAL_CFLAGS += ${bootanimation_CommonCFlags} + +LOCAL_SRC_FILES:= \ + BootAnimation.cpp LOCAL_C_INCLUDES += \ external/tinyalsa/include \ @@ -25,16 +60,11 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv1_CM \ libgui \ - libOpenSLES \ libtinyalsa \ libbase -LOCAL_MODULE:= bootanimation - -LOCAL_INIT_RC := bootanim.rc - ifdef TARGET_32_BIT_SURFACEFLINGER LOCAL_32_BIT_ONLY := true endif -include $(BUILD_EXECUTABLE) +include ${BUILD_SHARED_LIBRARY} diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 7394490fc8d4..6b2de4b7f1ff 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -62,7 +62,6 @@ #include <EGL/eglext.h> #include "BootAnimation.h" -#include "audioplay.h" namespace android { @@ -92,26 +91,18 @@ static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS; static const int TEXT_CENTER_VALUE = INT_MAX; static const int TEXT_MISSING_VALUE = INT_MIN; static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; -static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound"; static const int ANIM_ENTRY_NAME_MAX = 256; static constexpr size_t TEXT_POS_LEN_MAX = 16; -static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed"; -static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason"; -// bootreasons list in "system/core/bootstat/bootstat.cpp". -static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST { - "kernel_panic", - "Panic", - "Watchdog", -}; // --------------------------------------------------------------------------- -BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), - mTimeFormat12Hour(false), mTimeCheckThread(NULL) { +BootAnimation::BootAnimation(InitCallback initCallback, + PlayPartCallback partCallback) + : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), + mTimeFormat12Hour(false), mTimeCheckThread(NULL), + mInitCallback(initCallback), mPlayPartCallback(partCallback) { mSession = new SurfaceComposerClient(); - // If the system has already booted, the animation is not being used for a boot. - mSystemBoot = !android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false); std::string powerCtl = android::base::GetProperty("sys.powerctl", ""); if (powerCtl.empty()) { mShuttingDown = false; @@ -142,7 +133,6 @@ void BootAnimation::binderDied(const wp<IBinder>&) // might be blocked on a condition variable that will never be updated. kill( getpid(), SIGKILL ); requestExit(); - audioplay::destroy(); } status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, @@ -158,10 +148,6 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, asset->close(); delete asset; - // ensure we can call getPixels(). No need to call unlock, since the - // bitmap will go out of scope when we return from this method. - bitmap.lockPixels(); - const int w = bitmap.width(); const int h = bitmap.height(); const void* p = bitmap.getPixels(); @@ -216,10 +202,6 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) // the packed resource can be released. delete map; - // ensure we can call getPixels(). No need to call unlock, since the - // bitmap will go out of scope when we return from this method. - bitmap.lockPixels(); - const int w = bitmap.width(); const int h = bitmap.height(); const void* p = bitmap.getPixels(); @@ -712,7 +694,6 @@ bool BootAnimation::preloadZip(Animation& animation) return false; } - Animation::Part* partWithAudio = NULL; ZipEntryRO entry; char name[ANIM_ENTRY_NAME_MAX]; while ((entry = zip->nextEntry(cookie)) != NULL) { @@ -747,7 +728,6 @@ bool BootAnimation::preloadZip(Animation& animation) // a part may have at most one audio file part.audioData = (uint8_t *)map->getDataPtr(); part.audioLength = map->getDataLength(); - partWithAudio = ∂ } else if (leaf == "trim.txt") { part.trimData.setTo((char const*)map->getDataPtr(), map->getDataLength()); @@ -797,13 +777,8 @@ bool BootAnimation::preloadZip(Animation& animation) } } - // Create and initialize audioplay if there is a wav file in any of the animations. - // Do it on a separate thread so we don't hold up the animation intro. - if (partWithAudio != NULL) { - ALOGD("found audio.wav, creating playback engine"); - mInitAudioThread = new InitAudioThread(partWithAudio->audioData, - partWithAudio->audioLength); - mInitAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL); + if (mInitCallback != nullptr) { + mInitCallback(animation.parts); } zip->endIteration(cookie); @@ -876,11 +851,6 @@ bool BootAnimation::movie() mTimeCheckThread = nullptr; } - // We should have joined mInitAudioThread thread in playAnimation - if (mInitAudioThread != nullptr) { - mInitAudioThread = nullptr; - } - releaseAnimation(animation); if (clockFontInitialized) { @@ -917,14 +887,8 @@ bool BootAnimation::playAnimation(const Animation& animation) if(exitPending() && !part.playUntilComplete) break; - // only play audio file the first time we animate the part - if (r == 0 && part.audioData && playSoundsAllowed()) { - ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength); - // Block until the audio engine is finished initializing. - if (mInitAudioThread != nullptr) { - mInitAudioThread->join(); - } - audioplay::playClip(part.audioData, part.audioLength); + if (mPlayPartCallback != nullptr) { + mPlayPartCallback(i, part, r); } glClearColor( @@ -1013,10 +977,6 @@ bool BootAnimation::playAnimation(const Animation& animation) } } - // we've finally played everything we're going to play - audioplay::setPlaying(false); - audioplay::destroy(); - return true; } @@ -1062,32 +1022,6 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) return animation; } -bool BootAnimation::playSoundsAllowed() const { - // Only play sounds for system boots, not runtime restarts. - if (!mSystemBoot) { - return false; - } - if (mShuttingDown) { // no audio while shutting down - return false; - } - // Read the system property to see if we should play the sound. - // If it's not present, default to allowed. - if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) { - return false; - } - - // Don't play sounds if this is a reboot due to an error. - char bootreason[PROPERTY_VALUE_MAX]; - if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) { - for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) { - if (strcasecmp(str.c_str(), bootreason) == 0) { - return false; - } - } - } - return true; -} - bool BootAnimation::updateIsTimeAccurate() { 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 @@ -1219,17 +1153,6 @@ status_t BootAnimation::TimeCheckThread::readyToRun() { return NO_ERROR; } -BootAnimation::InitAudioThread::InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength) - : Thread(false), - mExampleAudioData(exampleAudioData), - mExampleAudioLength(exampleAudioLength) {} - -bool BootAnimation::InitAudioThread::threadLoop() { - audioplay::create(mExampleAudioData, mExampleAudioLength); - // Exit immediately - return false; -} - // --------------------------------------------------------------------------- } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 181ef1c596d1..3ebe7d6e4dff 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -39,44 +39,6 @@ class SurfaceControl; class BootAnimation : public Thread, public IBinder::DeathRecipient { public: - BootAnimation(); - - sp<SurfaceComposerClient> session() const; - -private: - virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef(); - virtual void binderDied(const wp<IBinder>& who); - - bool updateIsTimeAccurate(); - - class TimeCheckThread : public Thread { - public: - TimeCheckThread(BootAnimation* bootAnimation); - virtual ~TimeCheckThread(); - private: - virtual status_t readyToRun(); - virtual bool threadLoop(); - bool doThreadLoop(); - void addTimeDirWatch(); - - int mInotifyFd; - int mSystemWd; - int mTimeWd; - BootAnimation* mBootAnimation; - }; - - class InitAudioThread : public Thread { - public: - InitAudioThread(uint8_t* exampleAudioData, int mExampleAudioLength); - private: - virtual bool threadLoop(); - - uint8_t* mExampleAudioData; - int mExampleAudioLength; - }; - struct Texture { GLint w; GLint h; @@ -131,6 +93,49 @@ private: Font clockFont; }; + // Callback will be called during initialization after we have loaded + // the animation and be provided with all parts in animation. + typedef std::function<void(const Vector<Animation::Part>& parts)> InitCallback; + + // Callback will be called while animation is playing before each part is + // played. It will be provided with the part and play count for it. + // It will be provided with the partNumber for the part about to be played, + // as well as a reference to the part itself. It will also be provided with + // which play of that part is about to start, some parts are repeated + // multiple times. + typedef std::function<void(int partNumber, const Animation::Part& part, int playNumber)> + PlayPartCallback; + + // Callbacks may be null and will be called from this class's internal + // thread. + BootAnimation(InitCallback initCallback, PlayPartCallback partCallback); + + sp<SurfaceComposerClient> session() const; + +private: + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + virtual void binderDied(const wp<IBinder>& who); + + bool updateIsTimeAccurate(); + + class TimeCheckThread : public Thread { + public: + TimeCheckThread(BootAnimation* bootAnimation); + virtual ~TimeCheckThread(); + private: + virtual status_t readyToRun(); + virtual bool threadLoop(); + bool doThreadLoop(); + void addTimeDirWatch(); + + int mInotifyFd; + int mSystemWd; + int mTimeWd; + BootAnimation* mBootAnimation; + }; + status_t initTexture(Texture* texture, AssetManager& asset, const char* name); status_t initTexture(FileMap* map, int* width, int* height); status_t initFont(Font* font, const char* fallback); @@ -144,7 +149,6 @@ private: void releaseAnimation(Animation*) const; bool parseAnimationDesc(Animation&); bool preloadZip(Animation &animation); - bool playSoundsAllowed() const; void checkExit(); @@ -162,12 +166,12 @@ private: bool mClockEnabled; bool mTimeIsAccurate; bool mTimeFormat12Hour; - bool mSystemBoot; bool mShuttingDown; String8 mZipFileName; SortedVector<String8> mLoadedFiles; sp<TimeCheckThread> mTimeCheckThread = nullptr; - sp<InitAudioThread> mInitAudioThread = nullptr; + InitCallback mInitCallback = nullptr; + PlayPartCallback mPlayPartCallback = nullptr; }; // --------------------------------------------------------------------------- diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp index 3689d5ed937e..c11d90522ffd 100644 --- a/cmds/bootanimation/bootanimation_main.cpp +++ b/cmds/bootanimation/bootanimation_main.cpp @@ -27,13 +27,77 @@ #include <utils/Log.h> #include <utils/SystemClock.h> #include <utils/threads.h> +#include <android-base/properties.h> #include "BootAnimation.h" +#include "audioplay.h" using namespace android; // --------------------------------------------------------------------------- +namespace { + +// Create a typedef for readability. +typedef android::BootAnimation::Animation Animation; + +static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound"; +static const char BOOT_COMPLETED_PROP_NAME[] = "sys.boot_completed"; +static const char POWER_CTL_PROP_NAME[] = "sys.powerctl"; +static const char BOOTREASON_PROP_NAME[] = "ro.boot.bootreason"; +static const std::vector<std::string> PLAY_SOUND_BOOTREASON_BLACKLIST { + "kernel_panic", + "Panic", + "Watchdog", +}; + +class InitAudioThread : public Thread { +public: + InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength) + : Thread(false), + mExampleAudioData(exampleAudioData), + mExampleAudioLength(exampleAudioLength) {} +private: + virtual bool threadLoop() { + audioplay::create(mExampleAudioData, mExampleAudioLength); + // Exit immediately + return false; + } + + uint8_t* mExampleAudioData; + int mExampleAudioLength; +}; + +bool playSoundsAllowed() { + // Only play sounds for system boots, not runtime restarts. + if (android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false)) { + return false; + } + // no audio while shutting down + if (!android::base::GetProperty(POWER_CTL_PROP_NAME, "").empty()) { + return false; + } + // Read the system property to see if we should play the sound. + // If it's not present, default to allowed. + if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) { + return false; + } + + // Don't play sounds if this is a reboot due to an error. + char bootreason[PROPERTY_VALUE_MAX]; + if (property_get(BOOTREASON_PROP_NAME, bootreason, nullptr) > 0) { + for (const auto& str : PLAY_SOUND_BOOTREASON_BLACKLIST) { + if (strcasecmp(str.c_str(), bootreason) == 0) { + return false; + } + } + } + return true; +} + +} // namespace + + int main() { setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); @@ -71,10 +135,50 @@ int main() ALOGI("Waiting for SurfaceFlinger took %" PRId64 " ms", totalWaited); } + // TODO: Move audio code to a new class that just exports the callbacks. + sp<InitAudioThread> initAudioThread = nullptr; + + auto initCallback = [&](const Vector<Animation::Part>& parts) { + const Animation::Part* partWithAudio = nullptr; + for (const Animation::Part& part : parts) { + if (part.audioData != nullptr) { + partWithAudio = ∂ + } + } + + if (partWithAudio == nullptr) { + return; + } + + ALOGD("found audio.wav, creating playback engine"); + initAudioThread = new InitAudioThread(partWithAudio->audioData, + partWithAudio->audioLength); + initAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL); + + }; + + auto partCallback = [&](int partNumber, const Animation::Part& part, + int playNumber) { + // only play audio file the first time we animate the part + if (playNumber == 0 && part.audioData && playSoundsAllowed()) { + ALOGD("playing clip for part%d, size=%d", + partNumber, part.audioLength); + // Block until the audio engine is finished initializing. + if (initAudioThread != nullptr) { + initAudioThread->join(); + } + audioplay::playClip(part.audioData, part.audioLength); + } + }; + // create the boot animation object - sp<BootAnimation> boot = new BootAnimation(); + sp<BootAnimation> boot = new BootAnimation(initCallback, partCallback); IPCThreadState::self()->joinThreadPool(); + + // we've finally played everything we're going to play + audioplay::setPlaying(false); + audioplay::destroy(); } return 0; } diff --git a/cmds/hid/README.md b/cmds/hid/README.md new file mode 100644 index 000000000000..7e22d08eeaeb --- /dev/null +++ b/cmds/hid/README.md @@ -0,0 +1,145 @@ +# Usage +## Two options to use the hid command: +### 1. Interactive through stdin: +type `hid -` 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 HID events. +For an example, see the cts test case at: [InputTestCase.java][2] + +When using another program to control hid 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 behaviour. +In the current implementation of the hid command, the hid binary will wait +for the file descriptor to the uhid node to send the UHID_START and UHID_OPEN +signals before returning. However, this is not sufficient. These signals +only notify the readiness of the kernel driver, +but do not take into account the inputflinger framework. + + +### 2. Using a file as an input: +type `hid <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 hid binary. + +All of the input commands should be in pseudo-JSON format as documented below. +See examples [here][3]. + +The file can have multiple commands one after the other (which is not strictly +legal JSON format, as this would imply multiple root elements). + +## Command description + +1. `register` +Register a new uhid 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 | +| descriptor | byte array | USB HID report descriptor | + +Device ID is used for matching the subsequent commands to a specific device +to avoid ambiguity when multiple devices are registered. + +USB HID report descriptor should be generated according the the USB HID spec +and can be checked by reverse parsing using a variety of tools, for example +[usbdescreqparser][5]. + +Example: +```json +{ + "id": 1, + "command": "register", + "name": "Odie (Test)", + "vid": 0x18d1, + "pid": 0x2c40, + "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00, + 0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00, + 0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a, + 0x23, 0x02, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0b, 0x81, 0x02, 0x75, 0x01, 0x95, + 0x01, 0x81, 0x03, 0x05, 0x01, 0x75, 0x04, 0x95, 0x01, 0x25, 0x07, 0x46, 0x3b, 0x01, 0x66, + 0x14, 0x00, 0x09, 0x39, 0x81, 0x42, 0x66, 0x00, 0x00, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30, + 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x05, 0x02, 0x09, 0xc5, 0x09, 0xc4, 0x15, 0x00, 0x26, + 0xff, 0x00, 0x35, 0x00, 0x46, 0xff, 0x00, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0xc0, 0x85, + 0x02, 0x05, 0x08, 0x0a, 0x01, 0x00, 0x0a, 0x02, 0x00, 0x0a, 0x03, 0x00, 0x0a, 0x04, 0x00, + 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x04, 0x91, 0x02, 0x75, 0x04, 0x95, 0x01, 0x91, + 0x03, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x03, 0x05, 0x01, 0x09, 0x06, 0xa1, + 0x02, 0x05, 0x06, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, + 0x02, 0x06, 0xbc, 0xff, 0x0a, 0xad, 0xbd, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0xc0, 0xc0] +} +``` +2. `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 | + +Example: +```json +{ + "id": 1, + "command": "delay", + "duration": 10 +} +``` + +3. `report` +Send a report to the HID device + +| Field | Type | Description | +|:-------------:|:-------------:|:-------------------------- | +| id | integer | Device id | +| command | string | Must be set to "report" | +| report | byte array | Report data to send | + +Example: +```json +{ + "id": 1, + "command": "report", + "report": [0x01, 0x01, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00] +} +``` + +### Sending a joystick button press event +To send a button press event on a joystick device: +1. Register the joystick device +2. Send button down event with coordinates ABS_X, ABS_Y, ABS_Z, and ABS_RZ +at the center of the range. If the coordinates are not centered, this event +will generate a motion event within the input framework, in addition to the +button press event. The range can be determined from the uhid report descriptor. +3. Send the button up event with the same coordinates as in 2. +4. Check that the button press event was received. + +### 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 linux input subsystem does not generate events for those values +that remain unchanged. For example, if there are two events sent to the driver, +and both events have the same value of ABS_X, then ABS_X coordinate +will not be reported. +3. The description of joystick actions is available [here][6]. +4. Joysticks are split axes. When an analog stick is in a resting state, +the reported coordinates are at the center of the range. +5. 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]: https://developer.android.com/training/game-controllers/controller-input.html#button +[5]: http://eleccelerator.com/usbdescreqparser/ +[6]: https://developer.android.com/training/game-controllers/controller-input.html
\ No newline at end of file diff --git a/cmds/hid/jni/Android.mk b/cmds/hid/jni/Android.mk index d41d39d27f5b..86f4e012a943 100644 --- a/cmds/hid/jni/Android.mk +++ b/cmds/hid/jni/Android.mk @@ -6,14 +6,9 @@ LOCAL_SRC_FILES := \ com_android_commands_hid_Device.cpp LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ - frameworks/base/core/jni + $(JNI_H_INCLUDE) -LOCAL_SHARED_LIBRARIES := \ - libandroid_runtime \ - liblog \ - libnativehelper \ - libutils +LOCAL_LDLIBS += -landroid -llog -lnativehelper LOCAL_MODULE := libhidcommand_jni LOCAL_MODULE_TAGS := optional diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 1ea33ced7bbf..107dc863ef66 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -26,17 +26,17 @@ #include <memory> #include <unistd.h> -#include <android_runtime/AndroidRuntime.h> -#include <android_runtime/Log.h> -#include <android_os_MessageQueue.h> -#include <core_jni_helpers.h> #include <jni.h> #include <JNIHelp.h> #include <ScopedPrimitiveArray.h> #include <ScopedUtfChars.h> -#include <utils/Log.h> -#include <utils/Looper.h> -#include <utils/StrongPointer.h> +#include <android/looper.h> +#include <android/log.h> + +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) namespace android { namespace uhid { @@ -56,59 +56,67 @@ static int handleLooperEvents(int /* fd */, int events, void* data) { static void checkAndClearException(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { - ALOGE("An exception was thrown by callback '%s'.", methodName); - LOGE_EX(env); + LOGE("An exception was thrown by callback '%s'.", methodName); env->ExceptionClear(); } } DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) : - mCallbackObject(env->NewGlobalRef(callback)) { } + mCallbackObject(env->NewGlobalRef(callback)) { + env->GetJavaVM(&mJavaVM); + } DeviceCallback::~DeviceCallback() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJNIEnv(); env->DeleteGlobalRef(mCallbackObject); } void DeviceCallback::onDeviceError() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJNIEnv(); env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError); checkAndClearException(env, "onDeviceError"); } void DeviceCallback::onDeviceOpen() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJNIEnv(); env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen); checkAndClearException(env, "onDeviceOpen"); } +JNIEnv* DeviceCallback::getJNIEnv() { + JNIEnv* env; + mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); + return env; +} + Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize, - std::unique_ptr<DeviceCallback> callback, sp<Looper> looper) { + std::unique_ptr<DeviceCallback> callback) { int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC); if (fd < 0) { - ALOGE("Failed to open uhid: %s", strerror(errno)); + LOGE("Failed to open uhid: %s", strerror(errno)); return nullptr; } struct uhid_event ev; memset(&ev, 0, sizeof(ev)); - ev.type = UHID_CREATE; - strncpy((char*)ev.u.create.name, name, UHID_MAX_NAME_LENGTH); - ev.u.create.rd_data = descriptor.get(); - ev.u.create.rd_size = descriptorSize; - ev.u.create.bus = BUS_BLUETOOTH; - ev.u.create.vendor = vid; - ev.u.create.product = pid; - ev.u.create.version = 0; - ev.u.create.country = 0; + ev.type = UHID_CREATE2; + strncpy((char*)ev.u.create2.name, name, UHID_MAX_NAME_LENGTH); + memcpy(&ev.u.create2.rd_data, descriptor.get(), + descriptorSize * sizeof(ev.u.create2.rd_data[0])); + ev.u.create2.rd_size = descriptorSize; + ev.u.create2.bus = BUS_BLUETOOTH; + ev.u.create2.vendor = vid; + ev.u.create2.product = pid; + ev.u.create2.version = 0; + ev.u.create2.country = 0; errno = 0; ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev))); if (ret < 0 || ret != sizeof(ev)) { ::close(fd); - ALOGE("Failed to create uhid node: %s", strerror(errno)); + LOGE("Failed to create uhid node: %s", strerror(errno)); return nullptr; } @@ -116,20 +124,30 @@ Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev))); if (ret < 0 || ev.type != UHID_START) { ::close(fd); - ALOGE("uhid node failed to start: %s", strerror(errno)); + LOGE("uhid node failed to start: %s", strerror(errno)); return nullptr; } - - return new Device(id, fd, std::move(callback), looper); + return new Device(id, fd, std::move(callback)); } -Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback, sp<Looper> looper) : - mId(id), mFd(fd), mDeviceCallback(std::move(callback)), mLooper(looper) { - looper->addFd(fd, 0, Looper::EVENT_INPUT, handleLooperEvents, reinterpret_cast<void*>(this)); +Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback) : + mId(id), mFd(fd), mDeviceCallback(std::move(callback)) { + ALooper* aLooper = ALooper_forThread(); + if (aLooper == NULL) { + LOGE("Could not get ALooper, ALooper_forThread returned NULL"); + aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + } + ALooper_addFd(aLooper, fd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents, + reinterpret_cast<void*>(this)); } Device::~Device() { - mLooper->removeFd(mFd); + ALooper* looper = ALooper_forThread(); + if (looper != NULL) { + ALooper_removeFd(looper, mFd); + } else { + LOGE("Could not remove fd, ALooper_forThread() returned NULL!"); + } struct uhid_event ev; memset(&ev, 0, sizeof(ev)); ev.type = UHID_DESTROY; @@ -141,25 +159,25 @@ Device::~Device() { void Device::sendReport(uint8_t* report, size_t reportSize) { struct uhid_event ev; memset(&ev, 0, sizeof(ev)); - ev.type = UHID_INPUT; - ev.u.input.size = reportSize; - memcpy(&ev.u.input.data, report, reportSize); + ev.type = UHID_INPUT2; + ev.u.input2.size = reportSize; + memcpy(&ev.u.input2.data, report, reportSize); ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev))); if (ret < 0 || ret != sizeof(ev)) { - ALOGE("Failed to send hid event: %s", strerror(errno)); + LOGE("Failed to send hid event: %s", strerror(errno)); } } int Device::handleEvents(int events) { - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("uhid node was closed or an error occurred. events=0x%x", events); + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { + LOGE("uhid node was closed or an error occurred. events=0x%x", events); mDeviceCallback->onDeviceError(); return 0; } struct uhid_event ev; ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev))); if (ret < 0) { - ALOGE("Failed to read from uhid node: %s", strerror(errno)); + LOGE("Failed to read from uhid node: %s", strerror(errno)); mDeviceCallback->onDeviceError(); return 0; } @@ -184,7 +202,7 @@ std::unique_ptr<uint8_t[]> getData(JNIEnv* env, jbyteArray javaArray, size_t& ou } static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid, - jbyteArray rawDescriptor, jobject queue, jobject callback) { + jbyteArray rawDescriptor, jobject callback) { ScopedUtfChars name(env, rawName); if (name.c_str() == nullptr) { return 0; @@ -194,20 +212,21 @@ static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint i std::unique_ptr<uint8_t[]> desc = getData(env, rawDescriptor, size); std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback)); - sp<Looper> looper = android_os_MessageQueue_getMessageQueue(env, queue)->getLooper(); uhid::Device* d = uhid::Device::open( id, reinterpret_cast<const char*>(name.c_str()), vid, pid, - std::move(desc), size, std::move(cb), std::move(looper)); + std::move(desc), size, std::move(cb)); return reinterpret_cast<jlong>(d); } -static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr,jbyteArray rawReport) { +static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) { size_t size; std::unique_ptr<uint8_t[]> report = getData(env, rawReport, size); uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr); if (d) { d->sendReport(report.get(), size); + } else { + LOGE("Could not send report, Device* is null!"); } } @@ -220,7 +239,7 @@ static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { static JNINativeMethod sMethods[] = { { "nativeOpenDevice", - "(Ljava/lang/String;III[BLandroid/os/MessageQueue;" + "(Ljava/lang/String;III[B" "Lcom/android/commands/hid/Device$DeviceCallback;)J", reinterpret_cast<void*>(openDevice) }, { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) }, @@ -228,11 +247,21 @@ static JNINativeMethod sMethods[] = { }; int register_com_android_commands_hid_Device(JNIEnv* env) { - jclass clazz = FindClassOrDie(env, "com/android/commands/hid/Device$DeviceCallback"); + jclass clazz = env->FindClass("com/android/commands/hid/Device$DeviceCallback"); + if (clazz == NULL) { + LOGE("Unable to find class 'DeviceCallback'"); + return JNI_ERR; + } uhid::gDeviceCallbackClassInfo.onDeviceOpen = - GetMethodIDOrDie(env, clazz, "onDeviceOpen", "()V"); - uhid::gDeviceCallbackClassInfo.onDeviceError= - GetMethodIDOrDie(env, clazz, "onDeviceError", "()V"); + env->GetMethodID(clazz, "onDeviceOpen", "()V"); + uhid::gDeviceCallbackClassInfo.onDeviceError = + env->GetMethodID(clazz, "onDeviceError", "()V"); + if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL || + uhid::gDeviceCallbackClassInfo.onDeviceError == NULL) { + LOGE("Unable to obtain onDeviceOpen or onDeviceError methods"); + return JNI_ERR; + } + return jniRegisterNativeMethods(env, "com/android/commands/hid/Device", sMethods, NELEM(sMethods)); } diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index 6c5899eeb861..149456d8c10d 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -17,8 +17,6 @@ #include <memory> #include <jni.h> -#include <utils/Looper.h> -#include <utils/StrongPointer.h> namespace android { namespace uhid { @@ -32,16 +30,18 @@ public: void onDeviceError(); private: + JNIEnv* getJNIEnv(); jobject mCallbackObject; + JavaVM* mJavaVM; }; class Device { public: static Device* open(int32_t id, const char* name, int32_t vid, int32_t pid, std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize, - std::unique_ptr<DeviceCallback> callback, sp<Looper> looper); + std::unique_ptr<DeviceCallback> callback); - Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback, sp<Looper> looper); + Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback); ~Device(); void sendReport(uint8_t* report, size_t reportSize); @@ -53,7 +53,6 @@ private: int32_t mId; int mFd; std::unique_ptr<DeviceCallback> mDeviceCallback; - sp<Looper> mLooper; }; diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index dbe883bd1136..8c52a8ed1e09 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -29,22 +29,14 @@ import com.android.internal.os.SomeArgs; public class Device { private static final String TAG = "HidDevice"; - // Minimum amount of time to wait before sending input events to a device. Even though we're - // guaranteed that the device has been created and opened by the input system, there's still a - // window in which the system hasn't started reading events out of it. If a stream of events - // begins in during this window (like a button down event) and *then* we start reading, we're - // liable to ignore the whole stream. - private static final int MIN_WAIT_FOR_FIRST_EVENT = 150; - private static final int MSG_OPEN_DEVICE = 1; private static final int MSG_SEND_REPORT = 2; private static final int MSG_CLOSE_DEVICE = 3; - private final int mId; private final HandlerThread mThread; private final DeviceHandler mHandler; - private long mEventTime; + private long mTimeToSend; private final Object mCond = new Object(); @@ -53,7 +45,7 @@ public class Device { } private static native long nativeOpenDevice(String name, int id, int vid, int pid, - byte[] descriptor, MessageQueue queue, DeviceCallback callback); + byte[] descriptor, DeviceCallback callback); private static native void nativeSendReport(long ptr, byte[] data); private static native void nativeCloseDevice(long ptr); @@ -74,22 +66,22 @@ public class Device { args.arg2 = descriptor; args.arg3 = report; mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget(); - mEventTime = SystemClock.uptimeMillis() + MIN_WAIT_FOR_FIRST_EVENT; + mTimeToSend = SystemClock.uptimeMillis(); } public void sendReport(byte[] report) { Message msg = mHandler.obtainMessage(MSG_SEND_REPORT, report); - mHandler.sendMessageAtTime(msg, mEventTime); + // if two messages are sent at identical time, they will be processed in order received + mHandler.sendMessageAtTime(msg, mTimeToSend); } public void addDelay(int delay) { - mEventTime += delay; + mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay; } public void close() { Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE); - msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, mEventTime + 1); + mHandler.sendMessageAtTime(msg, Math.max(SystemClock.uptimeMillis(), mTimeToSend) + 1); try { synchronized (mCond) { mCond.wait(); @@ -111,8 +103,7 @@ public class Device { case MSG_OPEN_DEVICE: SomeArgs args = (SomeArgs) msg.obj; mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3, - (byte[]) args.arg2, getLooper().myQueue(), new DeviceCallback()); - nativeSendReport(mPtr, (byte[]) args.arg3); + (byte[]) args.arg2, new DeviceCallback()); pauseEvents(); break; case MSG_SEND_REPORT: @@ -155,6 +146,7 @@ public class Device { } public void onDeviceError() { + Log.e(TAG, "Device error occurred, closing /dev/uhid"); Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE); msg.setAsynchronous(true); msg.sendToTarget(); diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java index 976a78249bec..234e47f12dee 100644 --- a/cmds/hid/src/com/android/commands/hid/Hid.java +++ b/cmds/hid/src/com/android/commands/hid/Hid.java @@ -16,7 +16,6 @@ package com.android.commands.hid; -import android.os.SystemClock; import android.util.JsonReader; import android.util.JsonToken; import android.util.Log; @@ -91,7 +90,6 @@ public class Hid { } } - private void process(Event e) { final int index = mDevices.indexOfKey(e.getId()); if (index >= 0) { @@ -101,10 +99,16 @@ public class Hid { } else if (Event.COMMAND_REPORT.equals(e.getCommand())) { d.sendReport(e.getReport()); } else { - error("Unknown command \"" + e.getCommand() + "\". Ignoring event."); + if (Event.COMMAND_REGISTER.equals(e.getCommand())) { + error("Device id=" + e.getId() + " is already registered. Ignoring event."); + } else { + error("Unknown command \"" + e.getCommand() + "\". Ignoring event."); + } } - } else { + } else if (Event.COMMAND_REGISTER.equals(e.getCommand())) { registerDevice(e); + } else { + Log.e(TAG, "Unknown device id specified. Ignoring event."); } } @@ -124,7 +128,6 @@ public class Hid { } private static void error(String msg, Exception e) { - System.out.println(msg); Log.e(TAG, msg); if (e != null) { Log.e(TAG, Log.getStackTraceString(e)); diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index d71573f7ca50..c4193f647c32 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1473,7 +1473,7 @@ public final class Pm { ClearDataObserver obs = new ClearDataObserver(); try { mPm.freeStorageAndNotify(volumeUuid, sizeVal, - StorageManager.FLAG_ALLOCATE_DEFY_RESERVED, obs); + StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED, obs); synchronized (obs) { while (!obs.finished) { try { diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index e5c246608a0f..23668786abee 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -33,17 +33,24 @@ #include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> +#include <system/graphics.h> + // TODO: Fix Skia. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include <SkImageEncoder.h> #include <SkData.h> +#include <SkColorSpace.h> #pragma GCC diagnostic pop using namespace android; static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain; +#define COLORSPACE_UNKNOWN 0 +#define COLORSPACE_SRGB 1 +#define COLORSPACE_DISPLAY_P3 2 + static void usage(const char* pname) { fprintf(stderr, @@ -67,6 +74,31 @@ static SkColorType flinger2skia(PixelFormat f) } } +static sk_sp<SkColorSpace> dataSpaceToColorSpace(android_dataspace d) +{ + switch (d) { + case HAL_DATASPACE_V0_SRGB: + return SkColorSpace::MakeSRGB(); + case HAL_DATASPACE_DISPLAY_P3: + return SkColorSpace::MakeRGB( + SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut); + default: + return nullptr; + } +} + +static uint32_t dataSpaceToInt(android_dataspace d) +{ + switch (d) { + case HAL_DATASPACE_V0_SRGB: + return COLORSPACE_SRGB; + case HAL_DATASPACE_DISPLAY_P3: + return COLORSPACE_DISPLAY_P3; + default: + return COLORSPACE_UNKNOWN; + } +} + static status_t notifyMediaScanner(const char* fileName) { String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://"); String8 fileUrl("\""); @@ -139,6 +171,7 @@ int main(int argc, char** argv) void const* base = NULL; uint32_t w, s, h, f; + android_dataspace d; size_t size = 0; // Maps orientations from DisplayInfo to ISurfaceComposer @@ -177,13 +210,15 @@ int main(int argc, char** argv) h = screenshot.getHeight(); s = screenshot.getStride(); f = screenshot.getFormat(); + d = screenshot.getDataSpace(); size = screenshot.getSize(); } if (base != NULL) { if (png) { const SkImageInfo info = - SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType); + SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, + dataSpaceToColorSpace(d)); SkPixmap pixmap(info, base, s * bytesPerPixel(f)); struct FDWStream final : public SkWStream { size_t fBytesWritten = 0; @@ -200,9 +235,11 @@ int main(int argc, char** argv) notifyMediaScanner(fn); } } else { + uint32_t c = dataSpaceToInt(d); write(fd, &w, 4); write(fd, &h, 4); write(fd, &f, 4); + write(fd, &c, 4); size_t Bpp = bytesPerPixel(f); for (size_t y=0 ; y<h ; y++) { write(fd, base, w*Bpp); diff --git a/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk index 0c93b4c7b970..e6cbdb4ec49b 100644 --- a/cmds/uiautomator/instrumentation/Android.mk +++ b/cmds/uiautomator/instrumentation/Android.mk @@ -22,6 +22,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \ $(call all-java-files-under, ../library/core-src) LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit LOCAL_MODULE := uiautomator-instrumentation # TODO: change this to 18 when it's available LOCAL_SDK_VERSION := current diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk index e70bd1162fbb..4bf856f8cbfa 100644 --- a/cmds/uiautomator/library/Android.mk +++ b/cmds/uiautomator/library/Android.mk @@ -64,10 +64,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := android_uiautomator LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries) LOCAL_SOURCE_FILES_ALL_GENERATED := true -include $(BUILD_STATIC_JAVA_LIBRARY) # Make sure to run droiddoc first to generate the stub source files. -$(full_classes_compiled_jar) : $(uiautomator_stubs_stamp) -$(built_dex_intermediate) : $(uiautomator_stubs_stamp) +LOCAL_ADDITIONAL_DEPENDENCIES := $(uiautomator_stubs_stamp) +include $(BUILD_STATIC_JAVA_LIBRARY) ############################################### # API check |