summaryrefslogtreecommitdiff
path: root/cmds
diff options
context:
space:
mode:
Diffstat (limited to 'cmds')
-rw-r--r--cmds/bootanimation/BootAnimation.cpp2
-rw-r--r--cmds/bootanimation/FORMAT.md2
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.cpp28
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.h5
-rw-r--r--cmds/hid/src/com/android/commands/hid/Device.java10
-rw-r--r--cmds/hid/src/com/android/commands/hid/Event.java17
-rw-r--r--cmds/hid/src/com/android/commands/hid/Hid.java13
-rw-r--r--cmds/incident_helper/OWNERS1
-rw-r--r--cmds/incidentd/src/PrivacyFilter.cpp4
-rw-r--r--cmds/locksettings/TEST_MAPPING5
-rw-r--r--cmds/screencap/screencap.cpp286
-rw-r--r--cmds/telecom/Android.bp5
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java481
-rw-r--r--cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java10
-rw-r--r--cmds/uinput/README.md59
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Device.java14
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Event.java1
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java16
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Uinput.java1
19 files changed, 359 insertions, 601 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 77b74e9898b8..5adcd930e341 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -707,11 +707,11 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(mDisplay, mSurface);
- mFlingerSurfaceControl->updateDefaultBufferSize(newWidth, newHeight);
const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
mWidth = limitedSize.width;
mHeight = limitedSize.height;
+ mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight);
EGLConfig config = getEglConfig(mDisplay);
EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr);
if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) {
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index 01e8fe13fdf6..da8331af1492 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -126,7 +126,7 @@ the system property `service.bootanim.exit` to a nonzero string.)
Use `zopflipng` if you have it, otherwise `pngcrush` will do. e.g.:
for fn in *.png ; do
- zopflipng -m ${fn}s ${fn}s.new && mv -f ${fn}s.new ${fn}
+ zopflipng -m ${fn} ${fn}.new && mv -f ${fn}.new ${fn}
# or: pngcrush -q ....
done
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 8b8d361edbd4..a142450ac0c6 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -134,8 +134,9 @@ JNIEnv* DeviceCallback::getJNIEnv() {
return env;
}
-std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
- uint16_t bus, const std::vector<uint8_t>& descriptor,
+std::unique_ptr<Device> Device::open(int32_t id, const char* name, const char* uniq, int32_t vid,
+ int32_t pid, uint16_t bus,
+ const std::vector<uint8_t>& descriptor,
std::unique_ptr<DeviceCallback> callback) {
size_t size = descriptor.size();
if (size > HID_MAX_DESCRIPTOR_SIZE) {
@@ -152,8 +153,7 @@ std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid,
struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
- std::string uniq = android::base::StringPrintf("Id: %d", id);
- strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
+ strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq, sizeof(ev.u.create2.uniq));
memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
ev.u.create2.rd_size = size;
ev.u.create2.bus = bus;
@@ -314,19 +314,31 @@ std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) {
return data;
}
-static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
- jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
+static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jstring rawUniq, jint id,
+ jint vid, jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
ScopedUtfChars name(env, rawName);
if (name.c_str() == nullptr) {
return 0;
}
+ std::string uniq;
+ if (rawUniq != nullptr) {
+ uniq = ScopedUtfChars(env, rawUniq);
+ } else {
+ uniq = android::base::StringPrintf("Id: %d", id);
+ }
+
+ if (uniq.c_str() == nullptr) {
+ return 0;
+ }
+
std::vector<uint8_t> desc = getData(env, rawDescriptor);
std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
std::unique_ptr<uhid::Device> d =
- uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc,
+ uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()),
+ reinterpret_cast<const char*>(uniq.c_str()), vid, pid, bus, desc,
std::move(cb));
return reinterpret_cast<jlong>(d.release());
}
@@ -370,7 +382,7 @@ static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
static JNINativeMethod sMethods[] = {
{"nativeOpenDevice",
- "(Ljava/lang/String;IIII[B"
+ "(Ljava/lang/String;Ljava/lang/String;IIII[B"
"Lcom/android/commands/hid/Device$DeviceCallback;)J",
reinterpret_cast<void*>(openDevice)},
{"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 9c6060d837e0..bc7a9092cc4e 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -42,8 +42,9 @@ private:
class Device {
public:
- static std::unique_ptr<Device> open(int32_t id, const char* name, int32_t vid, int32_t pid,
- uint16_t bus, const std::vector<uint8_t>& descriptor,
+ static std::unique_ptr<Device> open(int32_t id, const char* name, const char* uniq, int32_t vid,
+ int32_t pid, uint16_t bus,
+ const std::vector<uint8_t>& descriptor,
std::unique_ptr<DeviceCallback> callback);
~Device();
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 0415037cfc9f..4e8adc3af55c 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -71,6 +71,7 @@ public class Device {
private static native long nativeOpenDevice(
String name,
+ String uniq,
int id,
int vid,
int pid,
@@ -89,6 +90,7 @@ public class Device {
public Device(
int id,
String name,
+ String uniq,
int vid,
int pid,
int bus,
@@ -113,8 +115,9 @@ public class Device {
} else {
args.arg1 = id + ":" + vid + ":" + pid;
}
- args.arg2 = descriptor;
- args.arg3 = report;
+ args.arg2 = uniq;
+ args.arg3 = descriptor;
+ args.arg4 = report;
mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget();
mTimeToSend = SystemClock.uptimeMillis();
}
@@ -167,11 +170,12 @@ public class Device {
mPtr =
nativeOpenDevice(
(String) args.arg1,
+ (String) args.arg2,
args.argi1,
args.argi2,
args.argi3,
args.argi4,
- (byte[]) args.arg2,
+ (byte[]) args.arg3,
new DeviceCallback());
pauseEvents();
break;
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 3efb79766b94..3b022796356b 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -56,6 +56,7 @@ public class Event {
private int mId;
private String mCommand;
private String mName;
+ private String mUniq;
private byte[] mDescriptor;
private int mVid;
private int mPid;
@@ -78,6 +79,10 @@ public class Event {
return mName;
}
+ public String getUniq() {
+ return mUniq;
+ }
+
public byte[] getDescriptor() {
return mDescriptor;
}
@@ -116,8 +121,9 @@ public class Event {
public String toString() {
return "Event{id=" + mId
- + ", command=" + String.valueOf(mCommand)
- + ", name=" + String.valueOf(mName)
+ + ", command=" + mCommand
+ + ", name=" + mName
+ + ", uniq=" + mUniq
+ ", descriptor=" + Arrays.toString(mDescriptor)
+ ", vid=" + mVid
+ ", pid=" + mPid
@@ -149,6 +155,10 @@ public class Event {
mEvent.mName = name;
}
+ public void setUniq(String uniq) {
+ mEvent.mUniq = uniq;
+ }
+
public void setDescriptor(byte[] descriptor) {
mEvent.mDescriptor = descriptor;
}
@@ -247,6 +257,9 @@ public class Event {
case "name":
eb.setName(mReader.nextString());
break;
+ case "uniq":
+ eb.setUniq(mReader.nextString());
+ break;
case "vid":
eb.setVid(readInt());
break;
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 2db791fe90bd..5ebfd959ef33 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -117,8 +117,17 @@ public class Hid {
"Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
}
int id = e.getId();
- Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
- e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
+ Device d = new Device(
+ id,
+ e.getName(),
+ e.getUniq(),
+ e.getVendorId(),
+ e.getProductId(),
+ e.getBus(),
+ e.getDescriptor(),
+ e.getReport(),
+ e.getFeatureReports(),
+ e.getOutputs());
mDevices.append(id, d);
}
diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS
index cede4eae50ad..29f44aba75d1 100644
--- a/cmds/incident_helper/OWNERS
+++ b/cmds/incident_helper/OWNERS
@@ -1,3 +1,2 @@
joeo@google.com
-kwekua@google.com
yanmin@google.com
diff --git a/cmds/incidentd/src/PrivacyFilter.cpp b/cmds/incidentd/src/PrivacyFilter.cpp
index 0d427d1021a6..b273fd469de3 100644
--- a/cmds/incidentd/src/PrivacyFilter.cpp
+++ b/cmds/incidentd/src/PrivacyFilter.cpp
@@ -195,7 +195,9 @@ status_t FieldStripper::strip(const uint8_t privacyPolicy) {
ProtoOutputStream proto(mEncodedBuffer);
// Optimization when no strip happens.
- if (mRestrictions == NULL || spec.RequireAll()) {
+ if (mRestrictions == NULL || spec.RequireAll()
+ // Do not iterate through fields if primitive data
+ || !mRestrictions->children /* != FieldDescriptor::TYPE_MESSAGE */) {
if (spec.CheckPremission(mRestrictions)) {
mSize = mData->size();
}
diff --git a/cmds/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING
index 7a449effdf76..af54a2decd89 100644
--- a/cmds/locksettings/TEST_MAPPING
+++ b/cmds/locksettings/TEST_MAPPING
@@ -11,5 +11,10 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases_LockSettings_NoFlakes"
+ }
]
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 917529ec1dcf..7e4f95bc9274 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -51,11 +51,13 @@ using namespace android;
void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
fprintf(stderr, R"(
-usage: %s [-hp] [-d display-id] [FILENAME]
+usage: %s [-ahp] [-d display-id] [FILENAME]
-h: this message
- -p: save the file as a png.
+ -a: captures all the active displays. This appends an integer postfix to the FILENAME.
+ e.g., FILENAME_0.png, FILENAME_1.png. If both -a and -d are given, it ignores -d.
-d: specify the display ID to capture%s
see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
+ -p: outputs in png format.
--hint-for-seamless If set will use the hintForSeamless path in SF
If FILENAME ends with .png it will be saved as a png.
@@ -63,11 +65,13 @@ If FILENAME is not given, the results will be printed to stdout.
)",
pname,
displayIdOpt
- .transform([](DisplayId id) {
- return std::string(ftl::Concat(" (default: ", id.value, ')').str());
- })
- .value_or(std::string())
- .c_str());
+ .transform([](DisplayId id) {
+ return std::string(ftl::Concat(
+ " (If the id is not given, it defaults to ", id.value,')'
+ ).str());
+ })
+ .value_or(std::string())
+ .c_str());
}
// For options that only exist in long-form. Anything in the
@@ -123,8 +127,8 @@ static status_t notifyMediaScanner(const char* fileName) {
int status;
int pid = fork();
if (pid < 0){
- fprintf(stderr, "Unable to fork in order to send intent for media scanner.\n");
- return UNKNOWN_ERROR;
+ fprintf(stderr, "Unable to fork in order to send intent for media scanner.\n");
+ return UNKNOWN_ERROR;
}
if (pid == 0){
int fd = open("/dev/null", O_WRONLY);
@@ -146,19 +150,119 @@ static status_t notifyMediaScanner(const char* fileName) {
return NO_ERROR;
}
+status_t capture(const DisplayId displayId,
+ const gui::CaptureArgs& captureArgs,
+ ScreenCaptureResults& outResult) {
+ sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ ScreenshotClient::captureDisplay(displayId, captureArgs, captureListener);
+
+ ScreenCaptureResults captureResults = captureListener->waitForResults();
+ if (!captureResults.fenceResult.ok()) {
+ fprintf(stderr, "Failed to take screenshot. Status: %d\n",
+ fenceStatus(captureResults.fenceResult));
+ return 1;
+ }
+
+ outResult = captureResults;
+
+ return 0;
+}
+
+status_t saveImage(const char* fn, bool png, const ScreenCaptureResults& captureResults) {
+ void* base = nullptr;
+ ui::Dataspace dataspace = captureResults.capturedDataspace;
+ sp<GraphicBuffer> buffer = captureResults.buffer;
+
+ status_t result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
+
+ if (base == nullptr || result != NO_ERROR) {
+ String8 reason;
+ if (result != NO_ERROR) {
+ reason.appendFormat(" Error Code: %d", result);
+ } else {
+ reason = "Failed to write to buffer";
+ }
+ fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
+ return 1;
+ }
+
+ int fd = -1;
+ if (fn == nullptr) {
+ fd = dup(STDOUT_FILENO);
+ if (fd == -1) {
+ fprintf(stderr, "Error writing to stdout. (%s)\n", strerror(errno));
+ return 1;
+ }
+ } else {
+ fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
+ if (fd == -1) {
+ fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
+ return 1;
+ }
+ }
+
+ if (png) {
+ AndroidBitmapInfo info;
+ info.format = flinger2bitmapFormat(buffer->getPixelFormat());
+ info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
+ info.width = buffer->getWidth();
+ info.height = buffer->getHeight();
+ info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
+
+ int result = AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base,
+ ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, &fd,
+ [](void* fdPtr, const void* data, size_t size) -> bool {
+ int bytesWritten = write(*static_cast<int*>(fdPtr),
+ data, size);
+ return bytesWritten == size;
+ });
+
+ if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
+ fprintf(stderr, "Failed to compress PNG (error code: %d)\n", result);
+ }
+
+ if (fn != NULL) {
+ notifyMediaScanner(fn);
+ }
+ } else {
+ uint32_t w = buffer->getWidth();
+ uint32_t h = buffer->getHeight();
+ uint32_t s = buffer->getStride();
+ uint32_t f = buffer->getPixelFormat();
+ uint32_t c = dataSpaceToInt(dataspace);
+
+ 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);
+ base = (void *)((char *)base + s*Bpp);
+ }
+ }
+ close(fd);
+
+ return 0;
+}
+
int main(int argc, char** argv)
{
- const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
- if (ids.empty()) {
+ const std::vector<PhysicalDisplayId> physicalDisplays =
+ SurfaceComposerClient::getPhysicalDisplayIds();
+
+ if (physicalDisplays.empty()) {
fprintf(stderr, "Failed to get ID for any displays.\n");
return 1;
}
std::optional<DisplayId> displayIdOpt;
+ std::vector<DisplayId> displaysToCapture;
gui::CaptureArgs captureArgs;
const char* pname = argv[0];
bool png = false;
+ bool all = false;
int c;
- while ((c = getopt_long(argc, argv, "phd:", LONG_OPTIONS, nullptr)) != -1) {
+ while ((c = getopt_long(argc, argv, "aphd:", LONG_OPTIONS, nullptr)) != -1) {
switch (c) {
case 'p':
png = true;
@@ -177,12 +281,17 @@ int main(int argc, char** argv)
fprintf(stderr, "Invalid display ID: Incorrect encoding.\n");
return 1;
}
+ displaysToCapture.push_back(displayIdOpt.value());
+ break;
+ }
+ case 'a': {
+ all = true;
break;
}
case '?':
case 'h':
- if (ids.size() == 1) {
- displayIdOpt = ids.front();
+ if (physicalDisplays.size() >= 1) {
+ displayIdOpt = physicalDisplays.front();
}
usage(pname, displayIdOpt);
return 1;
@@ -192,44 +301,52 @@ int main(int argc, char** argv)
}
}
- if (!displayIdOpt) {
- displayIdOpt = ids.front();
- if (ids.size() > 1) {
- fprintf(stderr,
- "[Warning] Multiple displays were found, but no display id was specified! "
- "Defaulting to the first display found, however this default is not guaranteed "
- "to be consistent across captures. A display id should be specified.\n");
- fprintf(stderr, "A display ID can be specified with the [-d display-id] option.\n");
- fprintf(stderr, "See \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n");
- }
- }
-
argc -= optind;
argv += optind;
- int fd = -1;
- const char* fn = NULL;
- if (argc == 0) {
- fd = dup(STDOUT_FILENO);
- } else if (argc == 1) {
- fn = argv[0];
- fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
- if (fd == -1) {
- fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
- return 1;
+ // We don't expect more than 2 arguments.
+ if (argc >= 2) {
+ if (physicalDisplays.size() >= 1) {
+ usage(pname, physicalDisplays.front());
+ } else {
+ usage(pname, std::nullopt);
}
- const int len = strlen(fn);
- if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
+ return 1;
+ }
+
+ std::string baseName;
+ std::string suffix;
+
+ if (argc == 1) {
+ std::string_view filename = { argv[0] };
+ if (filename.ends_with(".png")) {
+ baseName = filename.substr(0, filename.size()-4);
+ suffix = ".png";
png = true;
+ } else {
+ baseName = filename;
}
}
- if (fd == -1) {
- usage(pname, displayIdOpt);
- return 1;
+ if (all) {
+ // Ignores -d if -a is given.
+ displaysToCapture.clear();
+ for (int i = 0; i < physicalDisplays.size(); i++) {
+ displaysToCapture.push_back(physicalDisplays[i]);
+ }
}
- void* base = NULL;
+ if (displaysToCapture.empty()) {
+ displaysToCapture.push_back(physicalDisplays.front());
+ if (physicalDisplays.size() > 1) {
+ fprintf(stderr,
+ "[Warning] Multiple displays were found, but no display id was specified! "
+ "Defaulting to the first display found, however this default is not guaranteed "
+ "to be consistent across captures. A display id should be specified.\n");
+ fprintf(stderr, "A display ID can be specified with the [-d display-id] option.\n");
+ fprintf(stderr, "See \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n");
+ }
+ }
// setThreadPoolMaxThreadCount(0) actually tells the kernel it's
// not allowed to spawn any additional threads, but we still spawn
@@ -238,74 +355,39 @@ int main(int argc, char** argv)
ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
- sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- ScreenshotClient::captureDisplay(*displayIdOpt, captureArgs, 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;
+ std::vector<ScreenCaptureResults> results;
+ const size_t numDisplays = displaysToCapture.size();
+ for (int i=0; i<numDisplays; i++) {
+ ScreenCaptureResults result;
- status_t result = buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
+ // 1. Capture the screen
+ if (const status_t captureStatus =
+ capture(displaysToCapture[i], captureArgs, result) != 0) {
- if (base == nullptr || result != NO_ERROR) {
- String8 reason;
- if (result != NO_ERROR) {
- reason.appendFormat(" Error Code: %d", result);
- } else {
- reason = "Failed to write to buffer";
+ fprintf(stderr, "Capturing failed.\n");
+ return captureStatus;
}
- fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str());
- close(fd);
- return 1;
- }
-
- if (png) {
- AndroidBitmapInfo info;
- info.format = flinger2bitmapFormat(buffer->getPixelFormat());
- info.flags = ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
- info.width = buffer->getWidth();
- info.height = buffer->getHeight();
- info.stride = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
-
- int result = AndroidBitmap_compress(&info, static_cast<int32_t>(dataspace), base,
- ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100, &fd,
- [](void* fdPtr, const void* data, size_t size) -> bool {
- int bytesWritten = write(*static_cast<int*>(fdPtr),
- data, size);
- return bytesWritten == size;
- });
- if (result != ANDROID_BITMAP_RESULT_SUCCESS) {
- fprintf(stderr, "Failed to compress PNG (error code: %d)\n", result);
+ // 2. Save the capture result as an image.
+ // When there's more than one file to capture, add the index as postfix.
+ std::string filename;
+ if (!baseName.empty()) {
+ filename = baseName;
+ if (numDisplays > 1) {
+ filename += "_";
+ filename += std::to_string(i);
+ }
+ filename += suffix;
}
-
- if (fn != NULL) {
- notifyMediaScanner(fn);
+ const char* fn = nullptr;
+ if (!filename.empty()) {
+ fn = filename.c_str();
}
- } else {
- uint32_t w = buffer->getWidth();
- uint32_t h = buffer->getHeight();
- uint32_t s = buffer->getStride();
- uint32_t f = buffer->getPixelFormat();
- uint32_t c = dataSpaceToInt(dataspace);
-
- 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);
- base = (void *)((char *)base + s*Bpp);
+ if (const status_t saveImageStatus = saveImage(fn, png, result) != 0) {
+ fprintf(stderr, "Saving image failed.\n");
+ return saveImageStatus;
}
}
- close(fd);
return 0;
}
diff --git a/cmds/telecom/Android.bp b/cmds/telecom/Android.bp
index be027105ae98..494d2ae37d53 100644
--- a/cmds/telecom/Android.bp
+++ b/cmds/telecom/Android.bp
@@ -21,5 +21,8 @@ license {
java_binary {
name: "telecom",
wrapper: "telecom.sh",
- srcs: ["**/*.java"],
+ srcs: [
+ ":telecom-shell-commands-src",
+ "**/*.java",
+ ],
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 1488e14cfb8f..50af5a7a29b7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -17,30 +17,22 @@
package com.android.commands.telecom;
import android.app.ActivityThread;
-import android.content.ComponentName;
import android.content.Context;
-import android.net.Uri;
-import android.os.IUserManager;
import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.sysprop.TelephonyProperties;
-import android.telecom.Log;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import com.android.internal.os.BaseCommand;
import com.android.internal.telecom.ITelecomService;
+import com.android.server.telecom.TelecomShellCommand;
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.stream.Collectors;
+import java.io.FileDescriptor;
-public final class Telecom extends BaseCommand {
+/**
+ * @deprecated Use {@code com.android.server.telecom.TelecomShellCommand} instead and execute the
+ * shell command using {@code adb shell cmd telecom...}. This is only here for backwards
+ * compatibility reasons.
+ */
+@Deprecated
+public final class Telecom {
/**
* Command-line entry point.
@@ -52,458 +44,11 @@ public final class Telecom extends BaseCommand {
// TODO: Do it in zygote and RuntimeInit. b/148897549
ActivityThread.initializeMainlineModules();
- (new Telecom()).run(args);
- }
- private static final String CALLING_PACKAGE = Telecom.class.getPackageName();
- private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
- private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
- private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
- private static final String COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT =
- "set-user-selected-outgoing-phone-account";
- private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
- private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
- private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
- private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
- "add-or-remove-call-companion-app";
- private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
- "set-phone-acct-suggestion-component";
- private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
- private static final String COMMAND_SET_CALL_DIAGNOSTIC_SERVICE = "set-call-diagnostic-service";
- private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
- private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
- private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
- private static final String COMMAND_CLEANUP_STUCK_CALLS = "cleanup-stuck-calls";
- 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,
- * Example: adb shell telecom set-system-dialer <PACKAGE>
- *
- * Restore it to the default if if argument is "default" or no argument is passed.
- * Example: adb shell telecom set-system-dialer default
- */
- private static final String COMMAND_SET_SYSTEM_DIALER = "set-system-dialer";
- private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer";
- private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers";
- private static final String COMMAND_SET_SIM_COUNT = "set-sim-count";
- private static final String COMMAND_GET_SIM_CONFIG = "get-sim-config";
- private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
- private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER =
- "set-test-emergency-phone-account-package-filter";
- /**
- * Command used to emit a distinct "mark" in the logs.
- */
- private static final String COMMAND_LOG_MARK = "log-mark";
-
- private ComponentName mComponent;
- private String mAccountId;
- private ITelecomService mTelecomService;
- private TelephonyManager mTelephonyManager;
- private IUserManager mUserManager;
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println("usage: telecom [subcommand] [options]\n"
- + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
- + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
- + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
- + "usage: telecom register-sim-phone-account [-e] <COMPONENT> <ID> <USER_SN>"
- + " <LABEL>: registers a PhoneAccount with CAPABILITY_SIM_SUBSCRIPTION"
- + " and optionally CAPABILITY_PLACE_EMERGENCY_CALLS if \"-e\" is provided\n"
- + "usage: telecom set-user-selected-outgoing-phone-account [-e] <COMPONENT> <ID> "
- + "<USER_SN>\n"
- + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
- + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
- + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
- + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
- + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
- + " <LABEL> <ADDRESS>\n"
- + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
- + "usage: telecom set-call-diagnostic-service <PACKAGE>\n"
- + "usage: telecom set-default-dialer <PACKAGE>\n"
- + "usage: telecom get-default-dialer\n"
- + "usage: telecom get-system-dialer\n"
- + "usage: telecom wait-on-handlers\n"
- + "usage: telecom set-sim-count <COUNT>\n"
- + "usage: telecom get-sim-config\n"
- + "usage: telecom get-max-phones\n"
- + "usage: telecom stop-block-suppression: Stop suppressing the blocked number"
- + " provider after a call to emergency services.\n"
- + "usage: telecom cleanup-stuck-calls: Clear any disconnected calls that have"
- + " gotten wedged in Telecom.\n"
- + "usage: telecom cleanup-orphan-phone-accounts: remove any phone accounts that"
- + " no longer have a valid UserHandle or accounts that no longer belongs to an"
- + " installed package.\n"
- + "usage: telecom set-emer-phone-account-filter <PACKAGE>\n"
- + "\n"
- + "telecom set-phone-account-enabled: Enables the given phone account, if it has"
- + " already been registered with Telecom.\n"
- + "\n"
- + "telecom set-phone-account-disabled: Disables the given phone account, if it"
- + " has already been registered with telecom.\n"
- + "\n"
- + "telecom set-call-diagnostic-service: overrides call diagnostic service.\n"
- + "telecom set-default-dialer: Sets the override default dialer to the given"
- + " component; this will override whatever the dialer role is set to.\n"
- + "\n"
- + "telecom get-default-dialer: Displays the current default dialer.\n"
- + "\n"
- + "telecom get-system-dialer: Displays the current system dialer.\n"
- + "telecom set-system-dialer: Set the override system dialer to the given"
- + " component. To remove the override, send \"default\"\n"
- + "\n"
- + "telecom wait-on-handlers: Wait until all handlers finish their work.\n"
- + "\n"
- + "telecom set-sim-count: Set num SIMs (2 for DSDS, 1 for single SIM."
- + " This may restart the device.\n"
- + "\n"
- + "telecom get-sim-config: Get the mSIM config string. \"DSDS\" for DSDS mode,"
- + " or \"\" for single SIM\n"
- + "\n"
- + "telecom get-max-phones: Get the max supported phones from the modem.\n"
- + "telecom set-test-emergency-phone-account-package-filter <PACKAGE>: sets a"
- + " package name that will be used for test emergency calls. To clear,"
- + " send an empty package name. Real emergency calls will still be placed"
- + " 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"
- );
- }
-
- @Override
- public void onRun() throws Exception {
- mTelecomService = ITelecomService.Stub.asInterface(
- ServiceManager.getService(Context.TELECOM_SERVICE));
- if (mTelecomService == null) {
- Log.w(this, "onRun: Can't access telecom manager.");
- showError("Error: Could not access the Telecom Manager. Is the system running?");
- return;
- }
-
Looper.prepareMainLooper();
+ ITelecomService service = ITelecomService.Stub.asInterface(
+ ServiceManager.getService(Context.TELECOM_SERVICE));
Context context = ActivityThread.systemMain().getSystemContext();
- mTelephonyManager = context.getSystemService(TelephonyManager.class);
- if (mTelephonyManager == null) {
- Log.w(this, "onRun: Can't access telephony service.");
- showError("Error: Could not access the Telephony Service. Is the system running?");
- return;
- }
-
- mUserManager = IUserManager.Stub
- .asInterface(ServiceManager.getService(Context.USER_SERVICE));
- if (mUserManager == null) {
- Log.w(this, "onRun: Can't access user manager.");
- showError("Error: Could not access the User Manager. Is the system running?");
- return;
- }
- Log.i(this, "onRun: parsing command.");
- String command = nextArgRequired();
- switch (command) {
- case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
- runSetPhoneAccountEnabled(true);
- break;
- case COMMAND_SET_PHONE_ACCOUNT_DISABLED:
- runSetPhoneAccountEnabled(false);
- break;
- case COMMAND_REGISTER_PHONE_ACCOUNT:
- runRegisterPhoneAccount();
- break;
- case COMMAND_SET_TEST_CALL_REDIRECTION_APP:
- runSetTestCallRedirectionApp();
- break;
- case COMMAND_SET_TEST_CALL_SCREENING_APP:
- runSetTestCallScreeningApp();
- break;
- case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
- runAddOrRemoveCallCompanionApp();
- break;
- case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
- runSetTestPhoneAcctSuggestionComponent();
- break;
- case COMMAND_SET_CALL_DIAGNOSTIC_SERVICE:
- runSetCallDiagnosticService();
- break;
- case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
- runRegisterSimPhoneAccount();
- break;
- case COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT:
- runSetUserSelectedOutgoingPhoneAccount();
- break;
- case COMMAND_UNREGISTER_PHONE_ACCOUNT:
- runUnregisterPhoneAccount();
- break;
- case COMMAND_STOP_BLOCK_SUPPRESSION:
- runStopBlockSuppression();
- break;
- case COMMAND_CLEANUP_STUCK_CALLS:
- runCleanupStuckCalls();
- break;
- case COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS:
- runCleanupOrphanPhoneAccounts();
- break;
- case COMMAND_RESET_CAR_MODE:
- runResetCarMode();
- break;
- case COMMAND_SET_DEFAULT_DIALER:
- runSetDefaultDialer();
- break;
- case COMMAND_GET_DEFAULT_DIALER:
- runGetDefaultDialer();
- break;
- case COMMAND_SET_SYSTEM_DIALER:
- runSetSystemDialer();
- break;
- case COMMAND_GET_SYSTEM_DIALER:
- runGetSystemDialer();
- break;
- case COMMAND_WAIT_ON_HANDLERS:
- runWaitOnHandler();
- break;
- case COMMAND_SET_SIM_COUNT:
- runSetSimCount();
- break;
- case COMMAND_GET_SIM_CONFIG:
- runGetSimConfig();
- break;
- 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;
- case COMMAND_LOG_MARK:
- runLogMark();
- break;
- default:
- Log.w(this, "onRun: unknown command: %s", command);
- throw new IllegalArgumentException ("unknown command '" + command + "'");
- }
- }
-
- private void runSetPhoneAccountEnabled(boolean enabled) throws RemoteException {
- final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
- final boolean success = mTelecomService.enablePhoneAccount(handle, enabled);
- if (success) {
- System.out.println("Success - " + handle + (enabled ? " enabled." : " disabled."));
- } else {
- System.out.println("Error - is " + handle + " a valid PhoneAccount?");
- }
- }
-
- private void runRegisterPhoneAccount() throws RemoteException {
- final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
- final String label = nextArgRequired();
- PhoneAccount account = PhoneAccount.builder(handle, label)
- .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
- mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
- System.out.println("Success - " + handle + " registered.");
- }
-
- private void runRegisterSimPhoneAccount() throws RemoteException {
- boolean isEmergencyAccount = false;
- String opt;
- while ((opt = nextOption()) != null) {
- switch (opt) {
- case "-e": {
- isEmergencyAccount = true;
- break;
- }
- }
- }
- final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
- final String label = nextArgRequired();
- final String address = nextArgRequired();
- int capabilities = PhoneAccount.CAPABILITY_CALL_PROVIDER
- | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
- | (isEmergencyAccount ? PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS : 0);
- PhoneAccount account = PhoneAccount.builder(
- handle, label)
- .setAddress(Uri.parse(address))
- .setSubscriptionAddress(Uri.parse(address))
- .setCapabilities(capabilities)
- .setShortDescription(label)
- .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
- .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
- .build();
- mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
- System.out.println("Success - " + handle + " registered.");
- }
-
- private void runSetTestCallRedirectionApp() throws RemoteException {
- final String packageName = nextArg();
- mTelecomService.setTestDefaultCallRedirectionApp(packageName);
- }
-
- private void runSetTestCallScreeningApp() throws RemoteException {
- final String packageName = nextArg();
- mTelecomService.setTestDefaultCallScreeningApp(packageName);
- }
-
- private void runAddOrRemoveCallCompanionApp() throws RemoteException {
- final String packageName = nextArgRequired();
- String isAdded = nextArgRequired();
- boolean isAddedBool = "1".equals(isAdded);
- mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
- }
-
- private void runSetCallDiagnosticService() throws RemoteException {
- String packageName = nextArg();
- if ("default".equals(packageName)) packageName = null;
- mTelecomService.setTestCallDiagnosticService(packageName);
- System.out.println("Success - " + packageName + " set as call diagnostic service.");
- }
-
- private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
- final String componentName = nextArg();
- mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
- }
-
- private void runSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
- Log.i(this, "runSetUserSelectedOutgoingPhoneAccount");
- final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
- mTelecomService.setUserSelectedOutgoingPhoneAccount(handle);
- System.out.println("Success - " + handle + " set as default outgoing account.");
- }
-
- private void runUnregisterPhoneAccount() throws RemoteException {
- final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
- mTelecomService.unregisterPhoneAccount(handle, CALLING_PACKAGE);
- System.out.println("Success - " + handle + " unregistered.");
- }
-
- private void runStopBlockSuppression() throws RemoteException {
- mTelecomService.stopBlockSuppression();
- }
-
- private void runCleanupStuckCalls() throws RemoteException {
- mTelecomService.cleanupStuckCalls();
- }
-
- private void runCleanupOrphanPhoneAccounts() throws RemoteException {
- System.out.println("Success - cleaned up " + mTelecomService.cleanupOrphanPhoneAccounts()
- + " phone accounts.");
- }
-
- private void runResetCarMode() throws RemoteException {
- mTelecomService.resetCarMode();
- }
-
- private void runSetDefaultDialer() throws RemoteException {
- String packageName = nextArg();
- if ("default".equals(packageName)) packageName = null;
- mTelecomService.setTestDefaultDialer(packageName);
- System.out.println("Success - " + packageName + " set as override default dialer.");
- }
-
- private void runSetSystemDialer() throws RemoteException {
- final String flatComponentName = nextArg();
- final ComponentName componentName = (flatComponentName.equals("default")
- ? null : parseComponentName(flatComponentName));
- mTelecomService.setSystemDialer(componentName);
- System.out.println("Success - " + componentName + " set as override system dialer.");
- }
-
- private void runGetDefaultDialer() throws RemoteException {
- System.out.println(mTelecomService.getDefaultDialerPackage(CALLING_PACKAGE));
- }
-
- private void runGetSystemDialer() throws RemoteException {
- System.out.println(mTelecomService.getSystemDialerPackage(CALLING_PACKAGE));
- }
-
- private void runWaitOnHandler() throws RemoteException {
-
- }
-
- private void runSetSimCount() throws RemoteException {
- if (!callerIsRoot()) {
- System.out.println("set-sim-count requires adb root");
- return;
- }
- int numSims = Integer.parseInt(nextArgRequired());
- System.out.println("Setting sim count to " + numSims + ". Device may reboot");
- mTelephonyManager.switchMultiSimConfig(numSims);
- }
-
- /**
- * 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
- */
- private void runGetSimConfig() throws RemoteException {
- System.out.println(TelephonyProperties.multi_sim_config().orElse(""));
- }
-
- private void runGetMaxPhones() throws RemoteException {
- // how many logical modems can be potentially active simultaneously
- System.out.println(mTelephonyManager.getSupportedModemCount());
- }
-
- private void runSetEmergencyPhoneAccountPackageFilter() throws RemoteException {
- String packageName = mArgs.getNextArg();
- if (TextUtils.isEmpty(packageName)) {
- mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(null);
- System.out.println("Success - filter cleared");
- } else {
- mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(packageName);
- System.out.println("Success = filter set to " + packageName);
- }
-
- }
-
- private void runLogMark() throws RemoteException {
- String message = Arrays.stream(mArgs.peekRemainingArgs()).collect(Collectors.joining(" "));
- mTelecomService.requestLogMark(message);
- }
-
- private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
- if (TextUtils.isEmpty(mArgs.peekNextArg())) {
- return null;
- }
- final ComponentName component = parseComponentName(nextArgRequired());
- final String accountId = nextArgRequired();
- final String userSnInStr = nextArgRequired();
- UserHandle userHandle;
- try {
- final int userSn = Integer.parseInt(userSnInStr);
- userHandle = UserHandle.of(mUserManager.getUserHandle(userSn));
- } catch (NumberFormatException ex) {
- Log.w(this, "getPhoneAccountHandleFromArgs - invalid user %s", userSnInStr);
- throw new IllegalArgumentException ("Invalid user serial number " + userSnInStr);
- }
- return new PhoneAccountHandle(component, accountId, userHandle);
- }
-
- private boolean callerIsRoot() {
- return Process.ROOT_UID == Process.myUid();
- }
-
- private ComponentName parseComponentName(String component) {
- ComponentName cn = ComponentName.unflattenFromString(component);
- if (cn == null) {
- throw new IllegalArgumentException ("Invalid component " + component);
- }
- return cn;
+ new TelecomShellCommand(service, context).exec(null, FileDescriptor.in,
+ FileDescriptor.out, FileDescriptor.err, args);
}
}
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 488292d68620..f726361effd6 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -292,13 +292,17 @@ public class AccessibilityNodeInfoDumper {
int childCount = node.getChildCount();
for (int x = 0; x < childCount; x++) {
AccessibilityNodeInfo childNode = node.getChild(x);
-
+ if (childNode == null) {
+ continue;
+ }
if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty()
- || !safeCharSeqToString(childNode.getText()).isEmpty())
+ || !safeCharSeqToString(childNode.getText()).isEmpty()) {
return true;
+ }
- if (childNafCheck(childNode))
+ if (childNafCheck(childNode)) {
return true;
+ }
}
return false;
}
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index f1775864aca0..5d3f12e0d59f 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -154,8 +154,24 @@ will be unregistered. There is no explicit command for unregistering a device.
#### `delay`
-Add a delay between the processing of commands. The delay will be timed from when the last delay
-ended, rather than from the current time, to allow for more precise timings to be produced.
+Add a delay between the processing of commands.
+
+The delay will be timed relative to the time base, a reference time which is set when the device is
+registered or by the `updateTimeBase` command. Take the following set of example commands:
+
+1. `register` device
+2. `delay` 500ms
+3. `inject` some events
+4. `delay` 10ms
+5. `inject` more events
+
+If the `register` command is executed at time _X_, the injection at step 3 will be scheduled for
+time _X_+500ms. Since scheduling isn't precise, they might actually be injected a few milliseconds
+later, but regardless of that the injection at step 5 will always be scheduled for _X_+510ms. This
+prevents scheduling delays from building up over time and slowing down the playback of recordings.
+However, it does mean that when you expect to wait for an indeterminate period of time, you should
+send `updateTimeBase` afterwards to prevent following events being scheduled in the past — see that
+command's section for an example.
| Field | Type | Description |
|:-------------:|:-------------:|:-------------------------- |
@@ -173,6 +189,45 @@ Example:
}
```
+#### `updateTimeBase`
+
+Update the time base from which the following events are scheduled to the current time. When
+controlling `uinput` over standard input, you should send this command if you want following events
+to be scheduled relative to now, rather than the last injection. See the following example set of
+commands and the times they will be scheduled to run at:
+
+1. `register` (say this occurs at time _X_)
+2. `delay` 500ms
+3. `inject`: scheduled for _X_+500ms
+4. `delay` 10ms
+5. `inject`: scheduled for _X_+510ms
+6. (wait a few seconds)
+7. `updateTimeBase` (say this occurs at time _Y_)
+8. `delay` 10ms
+9. `inject`: scheduled for _Y_+10ms
+
+Without the `updateTimeBase` command, the final injection would be scheduled for _X_+520ms, which
+would be in the past.
+
+This is useful if you are issuing commands in multiple stages with long or unknown delays in between
+them. For example, say you have a test that does the following:
+
+1. `register` a device
+2. `inject` a few events that should launch an app
+3. Wait for the app to launch (an indeterminate amount of time, possibly seconds)
+4. 1000 `inject` commands separated by `delay` commands of 10ms
+
+Without `updateTimeBase`, the `inject` commands of step 4 will be scheduled to start immediately
+after the events from step 2. That time is probably in the past, so many of the 1000 injections will
+be sent immediately. This will likely fill the kernel's event buffers, causing events to be dropped.
+Sending `updateTimeBase` before the `inject` commands in step 4 will schedule them relative to the
+current time, meaning that they will be all injected with the intended 10ms delays between them.
+
+| Field | Type | Description |
+|:-------------:|:-------------:|:------------------------------- |
+| `id` | integer | Device ID |
+| `command` | string | Must be set to "updateTimeBase" |
+
#### `inject`
Send an array of uinput event packets to the uinput device
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index b452fc7094ba..2cac6313f4f5 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -102,7 +102,7 @@ public class Device {
}
mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
- mTimeToSendNanos = SystemClock.uptimeNanos();
+ updateTimeBase();
}
private long getTimeToSendMillis() {
@@ -135,6 +135,13 @@ public class Device {
}
/**
+ * Set the reference time from which future injections are scheduled to the current time.
+ */
+ public void updateTimeBase() {
+ mTimeToSendNanos = SystemClock.uptimeNanos();
+ }
+
+ /**
* Delay subsequent device activity by the specified amount of time.
*
* <p>Note that although the delay is specified in nanoseconds, due to limitations of {@link
@@ -216,7 +223,7 @@ public class Device {
break;
}
long offsetMicros = args.argl1;
- if (mLastInjectTimestampMicros == -1 || offsetMicros == -1) {
+ if (mLastInjectTimestampMicros == -1) {
// There's often a delay of a few milliseconds between the time specified to
// Handler.sendMessageAtTime and the handler actually being called, due to
// the way threads are scheduled. We don't take this into account when
@@ -232,6 +239,9 @@ public class Device {
// To prevent this, we need to use the time at which we scheduled this first
// batch, rather than the actual current time.
mLastInjectTimestampMicros = args.argl2 / 1000;
+ } else if (offsetMicros == -1) {
+ // No timestamp offset is specified for this event, so use the current time.
+ mLastInjectTimestampMicros = SystemClock.uptimeNanos() / 1000;
} else {
mLastInjectTimestampMicros += offsetMicros;
}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9e7ee0937efe..a3f3d1c1bc0e 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -36,6 +36,7 @@ public class Event {
DELAY,
INJECT,
SYNC,
+ UPDATE_TIME_BASE,
}
// Constants representing evdev event types, from include/uapi/linux/input-event-codes.h in the
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
index 6994f0cb0e4b..85a9e9b27bde 100644
--- a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.IntStream;
@@ -57,8 +58,7 @@ public class JsonStyleParser implements EventParser {
String name = mReader.nextName();
switch (name) {
case "id" -> eb.setId(readInt());
- case "command" -> eb.setCommand(
- Event.Command.valueOf(mReader.nextString().toUpperCase()));
+ case "command" -> eb.setCommand(readCommand());
case "name" -> eb.setName(mReader.nextString());
case "vid" -> eb.setVendorId(readInt());
case "pid" -> eb.setProductId(readInt());
@@ -91,6 +91,18 @@ public class JsonStyleParser implements EventParser {
return e;
}
+ private Event.Command readCommand() throws IOException {
+ String commandStr = mReader.nextString();
+ return switch (commandStr.toLowerCase(Locale.ROOT)) {
+ case "register" -> Event.Command.REGISTER;
+ case "delay" -> Event.Command.DELAY;
+ case "inject" -> Event.Command.INJECT;
+ case "sync" -> Event.Command.SYNC;
+ case "updatetimebase" -> Event.Command.UPDATE_TIME_BASE;
+ default -> throw new IllegalStateException("Invalid command \"" + commandStr + "\"");
+ };
+ }
+
private ArrayList<Integer> readInjectedEvents() throws IOException {
ArrayList<Integer> data = new ArrayList<>();
try {
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 760e981c8465..b9967fbdc3b6 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -137,6 +137,7 @@ public class Uinput {
case INJECT -> d.injectEvent(e.getInjections(), e.getTimestampOffsetMicros());
case DELAY -> d.addDelayNanos(e.getDurationNanos());
case SYNC -> d.syncEvent(e.getSyncToken());
+ case UPDATE_TIME_BASE -> d.updateTimeBase();
}
}