diff options
225 files changed, 4227 insertions, 2033 deletions
diff --git a/Android.mk b/Android.mk index 3b8d6a8ae8e5..58e21ffd1617 100644 --- a/Android.mk +++ b/Android.mk @@ -793,6 +793,8 @@ LOCAL_SRC_FILES := \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto) \ $(call all-proto-files-under, cmds/statsd/src) +# b/72714520 +LOCAL_ERROR_PRONE_FLAGS := -Xep:MissingOverride:OFF include $(BUILD_HOST_JAVA_LIBRARY) # ==== java proto device library (for test only) ============================== diff --git a/api/current.txt b/api/current.txt index df18f10243e0..a7a8f3ea2000 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36640,6 +36640,7 @@ package android.provider { field public static final deprecated java.lang.String RADIO_NFC = "nfc"; field public static final deprecated java.lang.String RADIO_WIFI = "wifi"; field public static final java.lang.String RINGTONE = "ringtone"; + field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode"; field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness"; field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1 @@ -38449,6 +38450,7 @@ package android.security.keystore { method public boolean isRandomizedEncryptionRequired(); method public boolean isStrongBoxBacked(); method public boolean isTrustedUserPresenceRequired(); + method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); @@ -38476,6 +38478,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...); method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean); + method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int); @@ -38567,6 +38570,8 @@ package android.security.keystore { method public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); method public boolean isRandomizedEncryptionRequired(); + method public boolean isTrustedUserPresenceRequired(); + method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); @@ -38585,6 +38590,8 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date); method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean); method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...); + method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean); + method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); @@ -40645,6 +40652,7 @@ package android.telecom { field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 + field public static final int PROPERTY_RTT = 1024; // 0x400 field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -49795,6 +49803,8 @@ package android.view.autofill { method public boolean isFieldClassificationEnabled(); method public void notifyValueChanged(android.view.View); method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue); + method public void notifyViewClicked(android.view.View); + method public void notifyViewClicked(android.view.View, int); method public void notifyViewEntered(android.view.View); method public void notifyViewEntered(android.view.View, int, android.graphics.Rect); method public void notifyViewExited(android.view.View); diff --git a/api/system-current.txt b/api/system-current.txt index 44349dc1a47e..8001ee3b7f6d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4164,6 +4164,8 @@ package android.provider { method public static void resetToDefaults(android.content.ContentResolver, java.lang.String); field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages"; field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; + field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent"; + field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis"; field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on"; field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess"; diff --git a/api/test-current.txt b/api/test-current.txt index 9bfc105efa8f..2e47e003a72c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1061,6 +1061,7 @@ package android.view.autofill { public final class AutofillId implements android.os.Parcelable { ctor public AutofillId(int); + ctor public AutofillId(android.view.autofill.AutofillId, int); } } diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS new file mode 100644 index 000000000000..1a68a32c4308 --- /dev/null +++ b/cmds/incident_helper/OWNERS @@ -0,0 +1,2 @@ +jinyithu@google.com +kwekua@google.com diff --git a/cmds/incidentd/.clang-format b/cmds/incidentd/.clang-format new file mode 100644 index 000000000000..6fa5b474b715 --- /dev/null +++ b/cmds/incidentd/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: Google +AllowShortIfStatementsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +BinPackArguments: true +BinPackParameters: true +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +IndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 +AccessModifierOffset: -4 +IncludeCategories: + - Regex: '^"Log\.h"' + Priority: -1 diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index 2b00d9e6a596..d2d24c89449a 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -32,7 +32,7 @@ LOCAL_SRC_FILES := \ src/Privacy.cpp \ src/Reporter.cpp \ src/Section.cpp \ - src/io_util.cpp \ + src/incidentd_util.cpp \ src/main.cpp \ src/report_directory.cpp @@ -56,6 +56,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ @@ -115,7 +116,7 @@ LOCAL_SRC_FILES := \ src/Privacy.cpp \ src/Reporter.cpp \ src/Section.cpp \ - src/io_util.cpp \ + src/incidentd_util.cpp \ src/report_directory.cpp \ tests/section_list.cpp \ tests/PrivacyBuffer_test.cpp \ diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS new file mode 100644 index 000000000000..1a68a32c4308 --- /dev/null +++ b/cmds/incidentd/OWNERS @@ -0,0 +1,2 @@ +jinyithu@google.com +kwekua@google.com diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md index 71c6deb18aac..1730a6401254 100644 --- a/cmds/incidentd/README.md +++ b/cmds/incidentd/README.md @@ -20,4 +20,8 @@ Run the test via AndroidTest.xml ``` root$ atest incidentd_test -```
\ No newline at end of file +``` + +Use clang-format to style the file + +clang-format -style=file -i <file list>
\ No newline at end of file diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index 0fff4e6dc4a0..883924c83d82 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "FdBuffer.h" @@ -26,30 +25,16 @@ #include <unistd.h> #include <wait.h> -const bool DEBUG = false; -const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB -const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max +const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB +const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max FdBuffer::FdBuffer() - :mBuffer(BUFFER_SIZE), - mStartTime(-1), - mFinishTime(-1), - mTimedOut(false), - mTruncated(false) -{ -} + : mBuffer(BUFFER_SIZE), mStartTime(-1), mFinishTime(-1), mTimedOut(false), mTruncated(false) {} -FdBuffer::~FdBuffer() -{ -} +FdBuffer::~FdBuffer() {} -status_t -FdBuffer::read(int fd, int64_t timeout) -{ - struct pollfd pfds = { - .fd = fd, - .events = POLLIN - }; +status_t FdBuffer::read(int fd, int64_t timeout) { + struct pollfd pfds = {.fd = fd, .events = POLLIN}; mStartTime = uptimeMillis(); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); @@ -63,22 +48,22 @@ FdBuffer::read(int fd, int64_t timeout) int64_t remainingTime = (mStartTime + timeout) - uptimeMillis(); if (remainingTime <= 0) { - if (DEBUG) ALOGD("timed out due to long read"); + VLOG("timed out due to long read"); mTimedOut = true; break; } int count = poll(&pfds, 1, remainingTime); if (count == 0) { - if (DEBUG) ALOGD("timed out due to block calling poll"); + VLOG("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { - if (DEBUG) ALOGD("poll failed: %s", strerror(errno)); + VLOG("poll failed: %s", strerror(errno)); return -errno; } else { if ((pfds.revents & POLLERR) != 0) { - if (DEBUG) ALOGD("return event has error %s", strerror(errno)); + VLOG("return event has error %s", strerror(errno)); return errno != 0 ? -errno : UNKNOWN_ERROR; } else { ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()); @@ -86,7 +71,7 @@ FdBuffer::read(int fd, int64_t timeout) if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; } else { - if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno)); + VLOG("Fail to read %d: %s", fd, strerror(errno)); return -errno; } } else if (amt == 0) { @@ -100,13 +85,12 @@ FdBuffer::read(int fd, int64_t timeout) return NO_ERROR; } -status_t -FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs) -{ +status_t FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, + const bool isSysfs) { struct pollfd pfds[] = { - { .fd = fd, .events = POLLIN }, - { .fd = toFd, .events = POLLOUT }, - { .fd = fromFd, .events = POLLIN }, + {.fd = fd, .events = POLLIN}, + {.fd = toFd, .events = POLLOUT}, + {.fd = fromFd, .events = POLLIN}, }; mStartTime = uptimeMillis(); @@ -131,7 +115,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis(); if (remainingTime <= 0) { - if (DEBUG) ALOGD("timed out due to long read"); + VLOG("timed out due to long read"); mTimedOut = true; break; } @@ -139,11 +123,11 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou // wait for any pfds to be ready to perform IO int count = poll(pfds, 3, remainingTime); if (count == 0) { - if (DEBUG) ALOGD("timed out due to block calling poll"); + VLOG("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { - if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno)); + VLOG("Fail to poll: %s", strerror(errno)); return -errno; } @@ -151,10 +135,10 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou for (int i = 0; i < 3; ++i) { if ((pfds[i].revents & POLLERR) != 0) { if (i == 0 && isSysfs) { - if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd); + VLOG("fd %d is sysfs, ignore its POLLERR return value", fd); continue; } - if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno)); + VLOG("fd[%d]=%d returns error events: %s", i, fd, strerror(errno)); return errno != 0 ? -errno : UNKNOWN_ERROR; } } @@ -169,9 +153,9 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou } if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno)); + VLOG("Fail to read fd %d: %s", fd, strerror(errno)); return -errno; - } // otherwise just continue + } // otherwise just continue } else if (amt == 0) { // reach EOF so don't have to poll pfds[0]. ::close(pfds[0].fd); pfds[0].fd = -1; @@ -191,9 +175,9 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou } if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno)); + VLOG("Fail to write toFd %d: %s", toFd, strerror(errno)); return -errno; - } // otherwise just continue + } // otherwise just continue } else { wpos += amt; cirSize -= amt; @@ -218,9 +202,9 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite()); if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno)); + VLOG("Fail to read fromFd %d: %s", fromFd, strerror(errno)); return -errno; - } // otherwise just continue + } // otherwise just continue } else if (amt == 0) { break; } else { @@ -232,14 +216,6 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou return NO_ERROR; } -size_t -FdBuffer::size() const -{ - return mBuffer.size(); -} +size_t FdBuffer::size() const { return mBuffer.size(); } -EncodedBuffer::iterator -FdBuffer::data() const -{ - return mBuffer.begin(); -} +EncodedBuffer::iterator FdBuffer::data() const { return mBuffer.begin(); } diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h index 48dc855e71b2..5bfa0938f5e8 100644 --- a/cmds/incidentd/src/FdBuffer.h +++ b/cmds/incidentd/src/FdBuffer.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef FD_BUFFER_H #define FD_BUFFER_H @@ -27,8 +28,7 @@ using namespace std; /** * Reads a file into a buffer, and then writes that data to an FdSet. */ -class FdBuffer -{ +class FdBuffer { public: FdBuffer(); ~FdBuffer(); @@ -50,7 +50,8 @@ public: * * Poll will return POLLERR if fd is from sysfs, handle this edge case. */ - status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false); + status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, + const bool isSysfs = false); /** * Whether we timed out. @@ -90,4 +91,4 @@ private: bool mTruncated; }; -#endif // FD_BUFFER_H +#endif // FD_BUFFER_H diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 654036ec6ab7..9ae624094dca 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -13,15 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "IncidentService.h" +#include "FdBuffer.h" +#include "PrivacyBuffer.h" #include "Reporter.h" +#include "incidentd_util.h" +#include "section_list.h" #include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> #include <binder/IServiceManager.h> +#include <binder/IShellCallback.h> #include <cutils/log.h> #include <private/android_filesystem_config.h> #include <utils/Looper.h> @@ -29,11 +34,9 @@ #include <unistd.h> using namespace android; +using namespace android::base; -enum { - WHAT_RUN_REPORT = 1, - WHAT_SEND_BACKLOG_TO_DROPBOX = 2 -}; +enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 }; //#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5) #define DEFAULT_BACKLOG_DELAY_NS (1000000000LL) @@ -42,9 +45,7 @@ enum { String16 const DUMP_PERMISSION("android.permission.DUMP"); String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS"); -static Status -checkIncidentPermissions(const IncidentReportArgs& args) -{ +static Status checkIncidentPermissions(const IncidentReportArgs& args) { uid_t callingUid = IPCThreadState::self()->getCallingUid(); pid_t callingPid = IPCThreadState::self()->getCallingPid(); if (callingUid == AID_ROOT || callingUid == AID_SHELL) { @@ -55,14 +56,16 @@ checkIncidentPermissions(const IncidentReportArgs& args) // checking calling permission. if (!checkCallingPermission(DUMP_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP", - callingPid, callingUid); - return Status::fromExceptionCode(Status::EX_SECURITY, + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, "Calling process does not have permission: android.permission.DUMP"); } if (!checkCallingPermission(USAGE_STATS_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS", - callingPid, callingUid); - return Status::fromExceptionCode(Status::EX_SECURITY, + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, "Calling process does not have permission: android.permission.USAGE_STATS"); } @@ -71,40 +74,34 @@ checkIncidentPermissions(const IncidentReportArgs& args) case DEST_LOCAL: if (callingUid != AID_SHELL && callingUid != AID_ROOT) { ALOGW("Calling pid %d and uid %d does not have permission to get local data.", - callingPid, callingUid); - return Status::fromExceptionCode(Status::EX_SECURITY, - "Calling process does not have permission to get local data."); + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, + "Calling process does not have permission to get local data."); } case DEST_EXPLICIT: - if (callingUid != AID_SHELL && callingUid != AID_ROOT && - callingUid != AID_STATSD && callingUid != AID_SYSTEM) { + if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD && + callingUid != AID_SYSTEM) { ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.", - callingPid, callingUid); - return Status::fromExceptionCode(Status::EX_SECURITY, - "Calling process does not have permission to get explicit data."); + callingPid, callingUid); + return Status::fromExceptionCode( + Status::EX_SECURITY, + "Calling process does not have permission to get explicit data."); } } return Status::ok(); } // ================================================================================ -ReportRequestQueue::ReportRequestQueue() -{ -} +ReportRequestQueue::ReportRequestQueue() {} -ReportRequestQueue::~ReportRequestQueue() -{ -} +ReportRequestQueue::~ReportRequestQueue() {} -void -ReportRequestQueue::addRequest(const sp<ReportRequest>& request) -{ +void ReportRequestQueue::addRequest(const sp<ReportRequest>& request) { unique_lock<mutex> lock(mLock); mQueue.push_back(request); } -sp<ReportRequest> -ReportRequestQueue::getNextRequest() -{ +sp<ReportRequest> ReportRequestQueue::getNextRequest() { unique_lock<mutex> lock(mLock); if (mQueue.empty()) { return NULL; @@ -115,22 +112,13 @@ ReportRequestQueue::getNextRequest() } } - // ================================================================================ ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue) - :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), - mHandlerLooper(handlerLooper), - mQueue(queue) -{ -} + : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), mHandlerLooper(handlerLooper), mQueue(queue) {} -ReportHandler::~ReportHandler() -{ -} +ReportHandler::~ReportHandler() {} -void -ReportHandler::handleMessage(const Message& message) -{ +void ReportHandler::handleMessage(const Message& message) { switch (message.what) { case WHAT_RUN_REPORT: run_report(); @@ -141,33 +129,24 @@ ReportHandler::handleMessage(const Message& message) } } -void -ReportHandler::scheduleRunReport(const sp<ReportRequest>& request) -{ +void ReportHandler::scheduleRunReport(const sp<ReportRequest>& request) { mQueue->addRequest(request); mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT); mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT)); } -void -ReportHandler::scheduleSendBacklogToDropbox() -{ +void ReportHandler::scheduleSendBacklogToDropbox() { unique_lock<mutex> lock(mLock); mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; schedule_send_backlog_to_dropbox_locked(); } -void -ReportHandler::schedule_send_backlog_to_dropbox_locked() -{ +void ReportHandler::schedule_send_backlog_to_dropbox_locked() { mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX); - mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, - Message(WHAT_SEND_BACKLOG_TO_DROPBOX)); + mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BACKLOG_TO_DROPBOX)); } -void -ReportHandler::run_report() -{ +void ReportHandler::run_report() { sp<Reporter> reporter = new Reporter(); // Merge all of the requests into one that has all of the @@ -190,15 +169,13 @@ ReportHandler::run_report() } } -void -ReportHandler::send_backlog_to_dropbox() -{ +void ReportHandler::send_backlog_to_dropbox() { if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) { // There was a failure. Exponential backoff. unique_lock<mutex> lock(mLock); mBacklogDelay *= 2; ALOGI("Error sending to dropbox. Trying again in %lld minutes", - (mBacklogDelay / (1000000000LL * 60))); + (mBacklogDelay / (1000000000LL * 60))); schedule_send_backlog_to_dropbox_locked(); } else { mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; @@ -207,18 +184,13 @@ ReportHandler::send_backlog_to_dropbox() // ================================================================================ IncidentService::IncidentService(const sp<Looper>& handlerLooper) - :mQueue(new ReportRequestQueue()) -{ + : mQueue(new ReportRequestQueue()) { mHandler = new ReportHandler(handlerLooper, mQueue); } -IncidentService::~IncidentService() -{ -} +IncidentService::~IncidentService() {} -Status -IncidentService::reportIncident(const IncidentReportArgs& args) -{ +Status IncidentService::reportIncident(const IncidentReportArgs& args) { ALOGI("reportIncident"); Status status = checkIncidentPermissions(args); @@ -231,10 +203,9 @@ IncidentService::reportIncident(const IncidentReportArgs& args) return Status::ok(); } -Status -IncidentService::reportIncidentToStream(const IncidentReportArgs& args, - const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream) -{ +Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args, + const sp<IIncidentReportStatusListener>& listener, + const unique_fd& stream) { ALOGI("reportIncidentToStream"); Status status = checkIncidentPermissions(args); @@ -252,12 +223,10 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args, return Status::ok(); } -Status -IncidentService::systemRunning() -{ +Status IncidentService::systemRunning() { if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { return Status::fromExceptionCode(Status::EX_SECURITY, - "Only system uid can call systemRunning"); + "Only system uid can call systemRunning"); } // When system_server is up and running, schedule the dropbox task to run. @@ -266,3 +235,120 @@ IncidentService::systemRunning() return Status::ok(); } +/** + * Implement our own because the default binder implementation isn't + * properly handling SHELL_COMMAND_TRANSACTION. + */ +status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + status_t err; + + switch (code) { + case SHELL_COMMAND_TRANSACTION: { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector<String8> args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(String8(data.readString16())); + } + sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder()); + sp<IResultReceiver> resultReceiver = + IResultReceiver::asInterface(data.readStrongBinder()); + + FILE* fin = fdopen(in, "r"); + FILE* fout = fdopen(out, "w"); + FILE* ferr = fdopen(err, "w"); + + if (fin == NULL || fout == NULL || ferr == NULL) { + resultReceiver->send(NO_MEMORY); + } else { + err = command(fin, fout, ferr, args); + resultReceiver->send(err); + } + + if (fin != NULL) { + fflush(fin); + fclose(fin); + } + if (fout != NULL) { + fflush(fout); + fclose(fout); + } + if (fout != NULL) { + fflush(ferr); + fclose(ferr); + } + + return NO_ERROR; + } + default: { return BnIncidentManager::onTransact(code, data, reply, flags); } + } +} + +status_t IncidentService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { + const int argCount = args.size(); + + if (argCount >= 1) { + if (!args[0].compare(String8("privacy"))) { + return cmd_privacy(in, out, err, args); + } + } + return cmd_help(out); +} + +status_t IncidentService::cmd_help(FILE* out) { + fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n"); + fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n"); + fprintf(out, " Prints/parses for the section id.\n"); + return NO_ERROR; +} + +static void printPrivacy(const Privacy* p, FILE* out, String8 indent) { + if (p == NULL) return; + fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->dest); + if (p->children == NULL) return; + for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated. + printPrivacy(p->children[i], out, indent + " "); + } +} + +status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { + const int argCount = args.size(); + if (argCount >= 3) { + String8 opt = args[1]; + int sectionId = atoi(args[2].string()); + + const Privacy* p = get_privacy_of_section(sectionId); + if (p == NULL) { + fprintf(err, "Can't find section id %d\n", sectionId); + return NO_ERROR; + } + fprintf(err, "Get privacy for %d\n", sectionId); + if (opt == "print") { + printPrivacy(p, out, String8("")); + } else if (opt == "parse") { + FdBuffer buf; + status_t error = buf.read(fileno(in), 60000); + if (error != NO_ERROR) { + fprintf(err, "Error reading from stdin\n"); + return error; + } + fprintf(err, "Read %zu bytes\n", buf.size()); + auto data = buf.data(); + PrivacyBuffer pBuf(p, data); + + PrivacySpec spec = PrivacySpec::new_spec(argCount > 3 ? atoi(args[3]) : -1); + error = pBuf.strip(spec); + if (error != NO_ERROR) { + fprintf(err, "Error strip pii fields with spec %d\n", spec.dest); + return error; + } + return pBuf.flush(fileno(out)); + } + } else { + return cmd_help(out); + } + return NO_ERROR; +} diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h index d6f33dfb1a86..3c665076bebc 100644 --- a/cmds/incidentd/src/IncidentService.h +++ b/cmds/incidentd/src/IncidentService.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef INCIDENT_SERVICE_H #define INCIDENT_SERVICE_H @@ -32,8 +33,7 @@ using namespace android::os; using namespace std; // ================================================================================ -class ReportRequestQueue : public virtual RefBase -{ +class ReportRequestQueue : public virtual RefBase { public: ReportRequestQueue(); virtual ~ReportRequestQueue(); @@ -46,10 +46,8 @@ private: deque<sp<ReportRequest> > mQueue; }; - // ================================================================================ -class ReportHandler : public MessageHandler -{ +class ReportHandler : public MessageHandler { public: ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue); virtual ~ReportHandler(); @@ -89,7 +87,6 @@ private: void send_backlog_to_dropbox(); }; - // ================================================================================ class IncidentService : public BnIncidentManager { public: @@ -99,14 +96,29 @@ public: virtual Status reportIncident(const IncidentReportArgs& args); virtual Status reportIncidentToStream(const IncidentReportArgs& args, - const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream); + const sp<IIncidentReportStatusListener>& listener, + const unique_fd& stream); virtual Status systemRunning(); + // Implement commands for debugging purpose. + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) override; + virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args); + private: sp<ReportRequestQueue> mQueue; sp<ReportHandler> mHandler; -}; + /** + * Commands print out help. + */ + status_t cmd_help(FILE* out); + + /** + * Commands related to privacy filtering. + */ + status_t cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args); +}; -#endif // INCIDENT_SERVICE_H +#endif // INCIDENT_SERVICE_H diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/Log.h index 90f543e30ff7..46efbd1fb7d0 100644 --- a/cmds/incidentd/src/io_util.cpp +++ b/cmds/incidentd/src/Log.h @@ -14,33 +14,21 @@ * limitations under the License. */ -#define LOG_TAG "incidentd" - -#include "io_util.h" - -#include <unistd.h> - -status_t write_all(int fd, uint8_t const* buf, size_t size) -{ - while (size > 0) { - ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size)); - if (amt < 0) { - return -errno; - } - size -= amt; - buf += amt; - } - return NO_ERROR; -} - -Fpipe::Fpipe() {} - -Fpipe::~Fpipe() { close(); } +/* + * This file must be included at the top of the file. Other header files + * occasionally include log.h, and if LOG_TAG isn't set when that happens + * we'll get a preprocesser error when we try to define it here. + */ -bool Fpipe::close() { return !(::close(mFds[0]) || ::close(mFds[1])); } +#pragma once -bool Fpipe::init() { return pipe(mFds) != -1; } +#define LOG_TAG "incidentd" +#define DEBUG false -int Fpipe::readFd() const { return mFds[0]; } +#include <log/log.h> -int Fpipe::writeFd() const { return mFds[1]; } +// Use the local value to turn on/off debug logs instead of using log.tag.properties. +// The advantage is that in production compiler can remove the logging code if the local +// DEBUG/VERBOSE is false. +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__);
\ No newline at end of file diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 3f0e331c8b55..c42a87b64a73 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -21,10 +21,9 @@ uint64_t encode_field_id(const Privacy* p) { return (uint64_t)p->type << 32 | p->field_id; } -const Privacy* lookup(const Privacy* p, uint32_t fieldId) -{ +const Privacy* lookup(const Privacy* p, uint32_t fieldId) { if (p->children == NULL) return NULL; - for (int i=0; p->children[i] != NULL; i++) { // NULL-terminated. + for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated. if (p->children[i]->field_id == fieldId) return p->children[i]; // Incident section gen tool guarantees field ids in ascending order. if (p->children[i]->field_id > fieldId) return NULL; @@ -32,52 +31,37 @@ const Privacy* lookup(const Privacy* p, uint32_t fieldId) return NULL; } -static bool allowDest(const uint8_t dest, const uint8_t policy) -{ +static bool allowDest(const uint8_t dest, const uint8_t policy) { switch (policy) { - case android::os::DEST_LOCAL: - return dest == android::os::DEST_LOCAL; - case android::os::DEST_EXPLICIT: - case DEST_UNSET: - return dest == android::os::DEST_LOCAL || - dest == android::os::DEST_EXPLICIT || - dest == DEST_UNSET; - case android::os::DEST_AUTOMATIC: - return true; - default: - return false; + case android::os::DEST_LOCAL: + return dest == android::os::DEST_LOCAL; + case android::os::DEST_EXPLICIT: + case DEST_UNSET: + return dest == android::os::DEST_LOCAL || dest == android::os::DEST_EXPLICIT || + dest == DEST_UNSET; + case android::os::DEST_AUTOMATIC: + return true; + default: + return false; } } -bool -PrivacySpec::operator<(const PrivacySpec& other) const -{ - return dest < other.dest; -} +bool PrivacySpec::operator<(const PrivacySpec& other) const { return dest < other.dest; } -bool -PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const -{ +bool PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const { uint8_t policy = privacy != NULL ? privacy->dest : defaultDest; return allowDest(dest, policy); } -bool -PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; } +bool PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; } -PrivacySpec PrivacySpec::new_spec(int dest) -{ +PrivacySpec PrivacySpec::new_spec(int dest) { switch (dest) { case android::os::DEST_AUTOMATIC: case android::os::DEST_EXPLICIT: case android::os::DEST_LOCAL: return PrivacySpec(dest); default: - return PrivacySpec(); + return PrivacySpec(android::os::DEST_AUTOMATIC); } } - -PrivacySpec PrivacySpec::get_default_dropbox_spec() -{ - return PrivacySpec(android::os::DEST_AUTOMATIC); -} diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h index 4f3db678f765..6b6de9cd9823 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef PRIVACY_H #define PRIVACY_H @@ -20,7 +21,7 @@ #include <stdint.h> // This is the default value of DEST enum, sync with privacy.proto -const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident +const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident const uint8_t DEST_DEFAULT_VALUE = DEST_UNSET; /* @@ -68,16 +69,17 @@ public: bool operator<(const PrivacySpec& other) const; // check permission of a policy, if returns true, don't strip the data. - bool CheckPremission(const Privacy* privacy, const uint8_t defaultDest = DEST_DEFAULT_VALUE) const; + bool CheckPremission(const Privacy* privacy, + const uint8_t defaultDest = DEST_DEFAULT_VALUE) const; // if returns true, no data need to be stripped. bool RequireAll() const; // Constructs spec using static methods below. static PrivacySpec new_spec(int dest); - static PrivacySpec get_default_dropbox_spec(); + private: PrivacySpec(uint8_t dest) : dest(dest) {} }; -#endif // PRIVACY_H +#endif // PRIVACY_H diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp index f53befefab93..e4128f4217d1 100644 --- a/cmds/incidentd/src/PrivacyBuffer.cpp +++ b/cmds/incidentd/src/PrivacyBuffer.cpp @@ -13,29 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "PrivacyBuffer.h" -#include "io_util.h" +#include "incidentd_util.h" +#include <android-base/file.h> #include <android/util/protobuf.h> #include <cutils/log.h> using namespace android::util; -const bool DEBUG = false; - /** * Write the field to buf based on the wire type, iterator will point to next field. * If skip is set to true, no data will be written to buf. Return number of bytes written. */ -void -PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) -{ - if (DEBUG) ALOGD("%s field %d (wiretype = %d)", skip ? "skip" : "write", - read_field_id(fieldTag), read_wire_type(fieldTag)); - +void PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) { uint8_t wireType = read_wire_type(fieldTag); size_t bytesToWrite = 0; uint32_t varint = 0; @@ -54,18 +47,17 @@ PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) break; case WIRE_TYPE_LENGTH_DELIMITED: bytesToWrite = mData.readRawVarint(); - if(!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite); + if (!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite); break; case WIRE_TYPE_FIXED32: if (!skip) mProto.writeRawVarint(fieldTag); bytesToWrite = 4; break; } - if (DEBUG) ALOGD("%s %d bytes of data", skip ? "skip" : "write", (int)bytesToWrite); if (skip) { mData.rp()->move(bytesToWrite); } else { - for (size_t i=0; i<bytesToWrite; i++) { + for (size_t i = 0; i < bytesToWrite; i++) { mProto.writeRawByte(mData.next()); } } @@ -78,28 +70,29 @@ PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) * The iterator must point to the head of a protobuf formatted field for successful operation. * After exit with NO_ERROR, iterator points to the next protobuf field's head. */ -status_t -PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec) -{ +status_t PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec, + int depth /* use as a counter for this recusive method. */) { if (!mData.hasNext() || parentPolicy == NULL) return BAD_VALUE; uint32_t fieldTag = mData.readRawVarint(); - const Privacy* policy = lookup(parentPolicy, read_field_id(fieldTag)); + uint32_t fieldId = read_field_id(fieldTag); + const Privacy* policy = lookup(parentPolicy, fieldId); + VLOG("[Depth %2d]Try to strip id %d, wiretype %d", depth, fieldId, read_wire_type(fieldTag)); if (policy == NULL || policy->children == NULL) { - if (DEBUG) ALOGD("Not a message field %d: dest(%d)", read_field_id(fieldTag), - policy != NULL ? policy->dest : parentPolicy->dest); - bool skip = !spec.CheckPremission(policy, parentPolicy->dest); // iterator will point to head of next field + size_t currentAt = mData.rp()->pos(); writeFieldOrSkip(fieldTag, skip); + VLOG("[Depth %2d]Field %d %ss %d bytes", depth, fieldId, skip ? "skip" : "write", + (int)(get_varint_size(fieldTag) + mData.rp()->pos() - currentAt)); return NO_ERROR; } // current field is message type and its sub-fields have extra privacy policies uint32_t msgSize = mData.readRawVarint(); - EncodedBuffer::Pointer start = mData.rp()->copy(); + size_t start = mData.rp()->pos(); long long token = mProto.start(encode_field_id(policy)); - while (mData.rp()->pos() - start.pos() != msgSize) { - status_t err = stripField(policy, spec); + while (mData.rp()->pos() - start != msgSize) { + status_t err = stripField(policy, spec, depth + 1); if (err != NO_ERROR) return err; } mProto.end(token); @@ -108,53 +101,39 @@ PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec) // ================================================================================ PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data) - :mPolicy(policy), - mData(data), - mProto(), - mSize(0) -{ -} + : mPolicy(policy), mData(data), mProto(), mSize(0) {} -PrivacyBuffer::~PrivacyBuffer() -{ -} +PrivacyBuffer::~PrivacyBuffer() {} -status_t -PrivacyBuffer::strip(const PrivacySpec& spec) -{ - if (DEBUG) ALOGD("Strip with spec %d", spec.dest); +status_t PrivacyBuffer::strip(const PrivacySpec& spec) { + VLOG("Strip with spec %d", spec.dest); // optimization when no strip happens if (mPolicy == NULL || mPolicy->children == NULL || spec.RequireAll()) { if (spec.CheckPremission(mPolicy)) mSize = mData.size(); return NO_ERROR; } while (mData.hasNext()) { - status_t err = stripField(mPolicy, spec); + status_t err = stripField(mPolicy, spec, 0); if (err != NO_ERROR) return err; } if (mData.bytesRead() != mData.size()) return BAD_VALUE; mSize = mProto.size(); - mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip. + mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip. return NO_ERROR; } -void -PrivacyBuffer::clear() -{ +void PrivacyBuffer::clear() { mSize = 0; mProto.clear(); } -size_t -PrivacyBuffer::size() const { return mSize; } +size_t PrivacyBuffer::size() const { return mSize; } -status_t -PrivacyBuffer::flush(int fd) -{ +status_t PrivacyBuffer::flush(int fd) { status_t err = NO_ERROR; EncodedBuffer::iterator iter = size() == mData.size() ? mData : mProto.data(); while (iter.readBuffer() != NULL) { - err = write_all(fd, iter.readBuffer(), iter.currentToRead()); + err = WriteFully(fd, iter.readBuffer(), iter.currentToRead()) ? NO_ERROR : -errno; iter.rp()->move(iter.currentToRead()); if (err != NO_ERROR) return err; } diff --git a/cmds/incidentd/src/PrivacyBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h index c9ca9a752c9c..92e1a2572465 100644 --- a/cmds/incidentd/src/PrivacyBuffer.h +++ b/cmds/incidentd/src/PrivacyBuffer.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef PRIVACY_BUFFER_H #define PRIVACY_BUFFER_H @@ -31,14 +32,14 @@ using namespace android::util; * PrivacyBuffer holds the original protobuf data and strips PII-sensitive fields * based on the request and holds stripped data in its own buffer for output. */ -class PrivacyBuffer -{ +class PrivacyBuffer { public: PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator& data); ~PrivacyBuffer(); /** - * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip succeeds. + * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip + * succeeds. */ status_t strip(const PrivacySpec& spec); @@ -64,8 +65,8 @@ private: ProtoOutputStream mProto; size_t mSize; - status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec); + status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec, int depth); void writeFieldOrSkip(uint32_t fieldTag, bool skip); }; -#endif // PRIVACY_BUFFER_H
\ No newline at end of file +#endif // PRIVACY_BUFFER_H
\ No newline at end of file diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index b9f479bd683f..c0b53586a8ce 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "Reporter.h" +#include "Privacy.h" #include "report_directory.h" #include "section_list.h" @@ -25,11 +25,11 @@ #include <private/android_filesystem_config.h> #include <utils/SystemClock.h> -#include <sys/types.h> -#include <sys/stat.h> #include <dirent.h> -#include <fcntl.h> #include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> /** * The directory where the incident reports are stored. @@ -38,72 +38,67 @@ static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/"; // ================================================================================ ReportRequest::ReportRequest(const IncidentReportArgs& a, - const sp<IIncidentReportStatusListener> &l, int f) - :args(a), - listener(l), - fd(f), - err(NO_ERROR) -{ -} + const sp<IIncidentReportStatusListener>& l, int f) + : args(a), listener(l), fd(f), err(NO_ERROR) {} -ReportRequest::~ReportRequest() -{ +ReportRequest::~ReportRequest() { if (fd >= 0) { // clean up the opened file descriptor close(fd); } } -bool -ReportRequest::ok() -{ - return fd >= 0 && err == NO_ERROR; -} +bool ReportRequest::ok() { return fd >= 0 && err == NO_ERROR; } // ================================================================================ ReportRequestSet::ReportRequestSet() - :mRequests(), - mSections(), - mMainFd(-1), - mMainDest(-1) -{ -} + : mRequests(), mSections(), mMainFd(-1), mMainDest(-1), mMetadata(), mSectionStats() {} -ReportRequestSet::~ReportRequestSet() -{ -} +ReportRequestSet::~ReportRequestSet() {} // TODO: dedup on exact same args and fd, report the status back to listener! -void -ReportRequestSet::add(const sp<ReportRequest>& request) -{ +void ReportRequestSet::add(const sp<ReportRequest>& request) { mRequests.push_back(request); mSections.merge(request->args); + mMetadata.set_request_size(mMetadata.request_size() + 1); } -void -ReportRequestSet::setMainFd(int fd) -{ +void ReportRequestSet::setMainFd(int fd) { mMainFd = fd; + mMetadata.set_use_dropbox(fd > 0); } -void -ReportRequestSet::setMainDest(int dest) -{ +void ReportRequestSet::setMainDest(int dest) { mMainDest = dest; + PrivacySpec spec = PrivacySpec::new_spec(dest); + switch (spec.dest) { + case android::os::DEST_AUTOMATIC: + mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC); + break; + case android::os::DEST_EXPLICIT: + mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT); + break; + case android::os::DEST_LOCAL: + mMetadata.set_dest(IncidentMetadata_Destination_LOCAL); + break; + } } -bool -ReportRequestSet::containsSection(int id) { - return mSections.containsSection(id); +bool ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); } + +IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) { + if (mSectionStats.find(id) == mSectionStats.end()) { + auto stats = mMetadata.add_sections(); + stats->set_id(id); + mSectionStats[id] = stats; + } + return mSectionStats[id]; } // ================================================================================ Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; }; -Reporter::Reporter(const char* directory) - :batch() -{ +Reporter::Reporter(const char* directory) : batch() { char buf[100]; // TODO: Make the max size smaller for user builds. @@ -121,22 +116,18 @@ Reporter::Reporter(const char* directory) mFilename = mIncidentDirectory + buf; } -Reporter::~Reporter() -{ -} - -Reporter::run_report_status_t -Reporter::runReport() -{ +Reporter::~Reporter() {} +Reporter::run_report_status_t Reporter::runReport() { status_t err = NO_ERROR; bool needMainFd = false; int mainFd = -1; int mainDest = -1; HeaderSection headers; + MetadataSection metadataSection; // See if we need the main file - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->fd < 0 && mainFd < 0) { needMainFd = true; mainDest = (*it)->args.dest(); @@ -167,7 +158,7 @@ Reporter::runReport() } // Tell everyone that we're starting. - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL) { (*it)->listener->onReportStarted(); } @@ -178,31 +169,36 @@ Reporter::runReport() // For each of the report fields, see if we need it, and if so, execute the command // and report to those that care that we're doing it. - for (const Section** section=SECTION_LIST; *section; section++) { + for (const Section** section = SECTION_LIST; *section; section++) { const int id = (*section)->id; if (this->batch.containsSection(id)) { ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string()); - // Notify listener of starting - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { - (*it)->listener->onReportSectionStatus(id, - IIncidentReportStatusListener::STATUS_STARTING); + (*it)->listener->onReportSectionStatus( + id, IIncidentReportStatusListener::STATUS_STARTING); } } // Execute - go get the data and write it into the file descriptors. + auto stats = batch.sectionStats(id); + int64_t startTime = uptimeMillis(); err = (*section)->Execute(&batch); + int64_t endTime = uptimeMillis(); + + stats->set_success(err == NO_ERROR); + stats->set_exec_duration_ms(endTime - startTime); if (err != NO_ERROR) { ALOGW("Incident section %s (%d) failed: %s. Stopping report.", - (*section)->name.string(), id, strerror(-err)); + (*section)->name.string(), id, strerror(-err)); goto DONE; } // Notify listener of starting - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { - (*it)->listener->onReportSectionStatus(id, - IIncidentReportStatusListener::STATUS_FINISHED); + (*it)->listener->onReportSectionStatus( + id, IIncidentReportStatusListener::STATUS_FINISHED); } } ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string()); @@ -210,13 +206,16 @@ Reporter::runReport() } DONE: + // Reports the metdadata when taking the incident report. + if (!isTest) metadataSection.Execute(&batch); + // Close the file. if (mainFd >= 0) { close(mainFd); } // Tell everyone that we're done. - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { + for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { if ((*it)->listener != NULL) { if (err == NO_ERROR) { (*it)->listener->onReportFinished(); @@ -238,7 +237,7 @@ DONE: // If the status was ok, delete the file. If not, leave it around until the next // boot or the next checkin. If the directory gets too big older files will // be rotated out. - if(!isTest) unlink(mFilename.c_str()); + if (!isTest) unlink(mFilename.c_str()); } return REPORT_FINISHED; @@ -247,9 +246,7 @@ DONE: /** * Create our output file and set the access permissions to -rw-rw---- */ -status_t -Reporter::create_file(int* fd) -{ +status_t Reporter::create_file(int* fd) { const char* filename = mFilename.c_str(); *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660); @@ -271,9 +268,7 @@ Reporter::create_file(int* fd) return NO_ERROR; } -Reporter::run_report_status_t -Reporter::upload_backlog() -{ +Reporter::run_report_status_t Reporter::upload_backlog() { DIR* dir; struct dirent* entry; struct stat st; @@ -324,4 +319,3 @@ Reporter::upload_backlog() return REPORT_FINISHED; } - diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index f30ecf0dd648..0f3f22172452 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -13,13 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef REPORTER_H #define REPORTER_H +#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h" + #include <android/os/IIncidentReportStatusListener.h> #include <android/os/IncidentReportArgs.h> +#include <map> #include <string> #include <vector> @@ -30,23 +34,21 @@ using namespace android::os; using namespace std; // ================================================================================ -struct ReportRequest : public virtual RefBase -{ +struct ReportRequest : public virtual RefBase { IncidentReportArgs args; sp<IIncidentReportStatusListener> listener; int fd; status_t err; - ReportRequest(const IncidentReportArgs& args, - const sp<IIncidentReportStatusListener> &listener, int fd); + ReportRequest(const IncidentReportArgs& args, const sp<IIncidentReportStatusListener>& listener, + int fd); virtual ~ReportRequest(); - bool ok(); // returns true if the request is ok for write. + bool ok(); // returns true if the request is ok for write. }; // ================================================================================ -class ReportRequestSet -{ +class ReportRequestSet { public: ReportRequestSet(); ~ReportRequestSet(); @@ -61,28 +63,31 @@ public: iterator end() { return mRequests.end(); } int mainFd() { return mMainFd; } - bool containsSection(int id); int mainDest() { return mMainDest; } + IncidentMetadata& metadata() { return mMetadata; } + + bool containsSection(int id); + IncidentMetadata::SectionStats* sectionStats(int id); + private: vector<sp<ReportRequest>> mRequests; IncidentReportArgs mSections; int mMainFd; int mMainDest; + + IncidentMetadata mMetadata; + map<int, IncidentMetadata::SectionStats*> mSectionStats; }; // ================================================================================ -class Reporter : public virtual RefBase -{ +class Reporter : public virtual RefBase { public: - enum run_report_status_t { - REPORT_FINISHED = 0, - REPORT_NEEDS_DROPBOX = 1 - }; + enum run_report_status_t { REPORT_FINISHED = 0, REPORT_NEEDS_DROPBOX = 1 }; ReportRequestSet batch; - Reporter(); - Reporter(const char* directory); + Reporter(); // PROD must use this constructor. + Reporter(const char* directory); // For testing purpose only. virtual ~Reporter(); // Run the report as described in the batch and args parameters. @@ -100,8 +105,7 @@ private: status_t create_file(int* fd); - bool isTest = true; // default to true for testing + bool isTest = true; // default to true for testing }; - -#endif // REPORTER_H +#endif // REPORTER_H diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index faeab87f8178..2e4e980278f9 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "Section.h" @@ -26,42 +25,41 @@ #include <memory> #include <mutex> +#include <android-base/file.h> #include <android/util/protobuf.h> #include <binder/IServiceManager.h> #include <log/log_event_list.h> -#include <log/logprint.h> #include <log/log_read.h> +#include <log/logprint.h> #include <private/android_logger.h> #include "FdBuffer.h" -#include "frameworks/base/core/proto/android/util/log.proto.h" -#include "io_util.h" #include "Privacy.h" #include "PrivacyBuffer.h" -#include "section_list.h" +#include "frameworks/base/core/proto/android/util/log.proto.h" +#include "incidentd_util.h" +using namespace android::base; using namespace android::util; using namespace std; // special section ids const int FIELD_ID_INCIDENT_HEADER = 1; +const int FIELD_ID_INCIDENT_METADATA = 2; // incident section parameters -const int WAIT_MAX = 5; +const int WAIT_MAX = 5; const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000}; const char INCIDENT_HELPER[] = "/system/bin/incident_helper"; -static pid_t -fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe) -{ - const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL }; +static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, + Fpipe& c2pPipe) { + const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL}; // fork used in multithreaded environment, avoid adding unnecessary code in child process pid_t pid = fork(); if (pid == 0) { - if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 - || !p2cPipe.close() - || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 - || !c2pPipe.close()) { + if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 || !p2cPipe.close() || + TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 || !c2pPipe.close()) { ALOGW("%s can't setup stdin and stdout for incident helper", name); _exit(EXIT_FAILURE); } @@ -72,7 +70,7 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi execv(INCIDENT_HELPER, const_cast<char**>(ihArgs)); ALOGW("%s failed in incident helper process: %s", name, strerror(errno)); - _exit(EXIT_FAILURE); // always exits with failure if any + _exit(EXIT_FAILURE); // always exits with failure if any } // close the fds used in incident helper close(p2cPipe.readFd()); @@ -83,18 +81,18 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi // ================================================================================ static status_t statusCode(int status) { if (WIFSIGNALED(status)) { - ALOGD("return by signal: %s", strerror(WTERMSIG(status))); - return -WTERMSIG(status); + VLOG("return by signal: %s", strerror(WTERMSIG(status))); + return -WTERMSIG(status); } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { - ALOGD("return by exit: %s", strerror(WEXITSTATUS(status))); - return -WEXITSTATUS(status); + VLOG("return by exit: %s", strerror(WEXITSTATUS(status))); + return -WEXITSTATUS(status); } return NO_ERROR; } static status_t kill_child(pid_t pid) { int status; - ALOGD("try to kill child process %d", pid); + VLOG("try to kill child process %d", pid); kill(pid, SIGKILL); if (waitpid(pid, &status, 0) == -1) return -1; return statusCode(status); @@ -104,7 +102,7 @@ static status_t wait_child(pid_t pid) { int status; bool died = false; // wait for child to report status up to 1 seconds - for(int loop = 0; !died && loop < WAIT_MAX; loop++) { + for (int loop = 0; !died && loop < WAIT_MAX; loop++) { if (waitpid(pid, &status, WNOHANG) == pid) died = true; // sleep for 0.2 second nanosleep(&WAIT_INTERVAL_NS, NULL); @@ -113,42 +111,24 @@ static status_t wait_child(pid_t pid) { return statusCode(status); } // ================================================================================ -static const Privacy* -get_privacy_of_section(int id) -{ - int l = 0; - int r = PRIVACY_POLICY_COUNT - 1; - while (l <= r) { - int mid = (l + r) >> 1; - const Privacy* p = PRIVACY_POLICY_LIST[mid]; - - if (p->field_id < (uint32_t)id) { - l = mid + 1; - } else if (p->field_id > (uint32_t)id) { - r = mid - 1; - } else { - return p; - } - } - return NULL; -} - -// ================================================================================ -static status_t -write_section_header(int fd, int sectionId, size_t size) -{ +static status_t write_section_header(int fd, int sectionId, size_t size) { uint8_t buf[20]; - uint8_t *p = write_length_delimited_tag_header(buf, sectionId, size); - return write_all(fd, buf, p-buf); + uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size); + return WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno; } -static status_t -write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* requests) -{ +static status_t write_report_requests(const int id, const FdBuffer& buffer, + ReportRequestSet* requests) { status_t err = -EBADF; EncodedBuffer::iterator data = buffer.data(); PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data); int writeable = 0; + auto stats = requests->sectionStats(id); + + stats->set_dump_size_bytes(data.size()); + stats->set_dump_duration_ms(buffer.durationMs()); + stats->set_timed_out(buffer.timedOut()); + stats->set_is_truncated(buffer.truncated()); // The streaming ones, group requests by spec in order to save unnecessary strip operations map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec; @@ -164,38 +144,49 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) { PrivacySpec spec = mit->first; err = privacyBuffer.strip(spec); - if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted. + if (err != NO_ERROR) return err; // it means the privacyBuffer data is corrupted. if (privacyBuffer.size() == 0) continue; for (auto it = mit->second.begin(); it != mit->second.end(); it++) { sp<ReportRequest> request = *it; err = write_section_header(request->fd, id, privacyBuffer.size()); - if (err != NO_ERROR) { request->err = err; continue; } + if (err != NO_ERROR) { + request->err = err; + continue; + } err = privacyBuffer.flush(request->fd); - if (err != NO_ERROR) { request->err = err; continue; } + if (err != NO_ERROR) { + request->err = err; + continue; + } writeable++; - ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, - privacyBuffer.size(), request->fd, spec.dest); + VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(), + request->fd, spec.dest); } privacyBuffer.clear(); } // The dropbox file if (requests->mainFd() >= 0) { - PrivacySpec spec = requests->mainDest() < 0 ? - PrivacySpec::get_default_dropbox_spec() : - PrivacySpec::new_spec(requests->mainDest()); + PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest()); err = privacyBuffer.strip(spec); - if (err != NO_ERROR) return err; // the buffer data is corrupted. + if (err != NO_ERROR) return err; // the buffer data is corrupted. if (privacyBuffer.size() == 0) goto DONE; err = write_section_header(requests->mainFd(), id, privacyBuffer.size()); - if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; } + if (err != NO_ERROR) { + requests->setMainFd(-1); + goto DONE; + } err = privacyBuffer.flush(requests->mainFd()); - if (err != NO_ERROR) { requests->setMainFd(-1); goto DONE; } + if (err != NO_ERROR) { + requests->setMainFd(-1); + goto DONE; + } writeable++; - ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id, - privacyBuffer.size(), requests->mainFd(), spec.dest); + VLOG("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(), + requests->mainFd(), spec.dest); + stats->set_report_size_bytes(privacyBuffer.size()); } DONE: @@ -204,67 +195,78 @@ DONE: } // ================================================================================ -Section::Section(int i, const int64_t timeoutMs) - :id(i), - timeoutMs(timeoutMs) -{ -} +Section::Section(int i, const int64_t timeoutMs) : id(i), timeoutMs(timeoutMs) {} -Section::~Section() -{ -} +Section::~Section() {} // ================================================================================ -HeaderSection::HeaderSection() - :Section(FIELD_ID_INCIDENT_HEADER, 0) -{ -} +HeaderSection::HeaderSection() : Section(FIELD_ID_INCIDENT_HEADER, 0) {} -HeaderSection::~HeaderSection() -{ -} +HeaderSection::~HeaderSection() {} -status_t -HeaderSection::Execute(ReportRequestSet* requests) const -{ - for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) { +status_t HeaderSection::Execute(ReportRequestSet* requests) const { + for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { const sp<ReportRequest> request = *it; const vector<vector<uint8_t>>& headers = request->args.headers(); - for (vector<vector<uint8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) { + for (vector<vector<uint8_t>>::const_iterator buf = headers.begin(); buf != headers.end(); + buf++) { if (buf->empty()) continue; // So the idea is only requests with negative fd are written to dropbox file. int fd = request->fd >= 0 ? request->fd : requests->mainFd(); - write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size()); - write_all(fd, (uint8_t const*)buf->data(), buf->size()); + write_section_header(fd, id, buf->size()); + WriteFully(fd, (uint8_t const*)buf->data(), buf->size()); // If there was an error now, there will be an error later and we will remove // it from the list then. } } return NO_ERROR; } +// ================================================================================ +MetadataSection::MetadataSection() : Section(FIELD_ID_INCIDENT_METADATA, 0) {} +MetadataSection::~MetadataSection() {} + +status_t MetadataSection::Execute(ReportRequestSet* requests) const { + std::string metadataBuf; + requests->metadata().SerializeToString(&metadataBuf); + for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { + const sp<ReportRequest> request = *it; + if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) { + continue; + } + write_section_header(request->fd, id, metadataBuf.size()); + if (!WriteFully(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size())) { + ALOGW("Failed to write metadata to fd %d", request->fd); + // we don't fail if we can't write to a single request's fd. + } + } + if (requests->mainFd() >= 0 && !metadataBuf.empty()) { + write_section_header(requests->mainFd(), id, metadataBuf.size()); + if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size())) { + ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd()); + return -1; + } + } + return NO_ERROR; +} // ================================================================================ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) - :Section(id, timeoutMs), - mFilename(filename) -{ + : Section(id, timeoutMs), mFilename(filename) { name = filename; mIsSysfs = strncmp(filename, "/sys/", 5) == 0; } FileSection::~FileSection() {} -status_t -FileSection::Execute(ReportRequestSet* requests) const -{ +status_t FileSection::Execute(ReportRequestSet* requests) const { // read from mFilename first, make sure the file is available // add O_CLOEXEC to make sure it is closed when exec incident helper int fd = open(mFilename, O_RDONLY | O_CLOEXEC); if (fd == -1) { - ALOGW("FileSection '%s' failed to open file", this->name.string()); - return -errno; + ALOGW("FileSection '%s' failed to open file", this->name.string()); + return -errno; } FdBuffer buffer; @@ -284,22 +286,23 @@ FileSection::Execute(ReportRequestSet* requests) const // parent process status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(), - this->timeoutMs, mIsSysfs); + this->timeoutMs, mIsSysfs); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); kill_child(pid); return readStatus; } status_t ihStatus = wait_child(pid); if (ihStatus != NO_ERROR) { - ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus)); + ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), + strerror(-ihStatus)); return ihStatus; } - ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), - (int)buffer.durationMs()); + VLOG("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), + (int)buffer.durationMs()); status_t err = write_report_requests(this->id, buffer, requests); if (err != NO_ERROR) { ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err)); @@ -310,8 +313,7 @@ FileSection::Execute(ReportRequestSet* requests) const } // ================================================================================ -struct WorkerThreadData : public virtual RefBase -{ +struct WorkerThreadData : public virtual RefBase { const WorkerThreadSection* section; int fds[2]; @@ -328,31 +330,19 @@ struct WorkerThreadData : public virtual RefBase }; WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec) - :section(sec), - workerDone(false), - workerError(NO_ERROR) -{ + : section(sec), workerDone(false), workerError(NO_ERROR) { fds[0] = -1; fds[1] = -1; } -WorkerThreadData::~WorkerThreadData() -{ -} +WorkerThreadData::~WorkerThreadData() {} // ================================================================================ -WorkerThreadSection::WorkerThreadSection(int id) - :Section(id) -{ -} +WorkerThreadSection::WorkerThreadSection(int id) : Section(id) {} -WorkerThreadSection::~WorkerThreadSection() -{ -} +WorkerThreadSection::~WorkerThreadSection() {} -static void* -worker_thread_func(void* cookie) -{ +static void* worker_thread_func(void* cookie) { WorkerThreadData* data = (WorkerThreadData*)cookie; status_t err = data->section->BlockingCall(data->writeFd()); @@ -368,9 +358,7 @@ worker_thread_func(void* cookie) return NULL; } -status_t -WorkerThreadSection::Execute(ReportRequestSet* requests) const -{ +status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { status_t err = NO_ERROR; pthread_t thread; pthread_attr_t attr; @@ -413,7 +401,7 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const if (err != NO_ERROR) { // TODO: Log this error into the incident report. ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(), - strerror(-err)); + strerror(-err)); } // Done with the read fd. The worker thread closes the write one so @@ -432,7 +420,7 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const err = data->workerError; // TODO: Log this error into the incident report. ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(), - strerror(-err)); + strerror(-err)); } } } @@ -450,13 +438,13 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const // just exit with a log messasge. if (err != NO_ERROR) { ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(), - strerror(-err)); + strerror(-err)); return NO_ERROR; } // Write the data that was collected - ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(), - (int)buffer.durationMs()); + VLOG("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(), + (int)buffer.durationMs()); err = write_report_requests(this->id, buffer, requests); if (err != NO_ERROR) { ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err)); @@ -467,14 +455,12 @@ WorkerThreadSection::Execute(ReportRequestSet* requests) const } // ================================================================================ -void -CommandSection::init(const char* command, va_list args) -{ +void CommandSection::init(const char* command, va_list args) { va_list copied_args; int numOfArgs = 0; va_copy(copied_args, args); - while(va_arg(copied_args, const char*) != NULL) { + while (va_arg(copied_args, const char*) != NULL) { numOfArgs++; } va_end(copied_args); @@ -484,41 +470,33 @@ CommandSection::init(const char* command, va_list args) mCommand[0] = command; name = command; - for (int i=0; i<numOfArgs; i++) { + for (int i = 0; i < numOfArgs; i++) { const char* arg = va_arg(args, const char*); - mCommand[i+1] = arg; + mCommand[i + 1] = arg; name += " "; name += arg; } - mCommand[numOfArgs+1] = NULL; + mCommand[numOfArgs + 1] = NULL; } CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...) - :Section(id, timeoutMs) -{ + : Section(id, timeoutMs) { va_list args; va_start(args, command); init(command, args); va_end(args); } -CommandSection::CommandSection(int id, const char* command, ...) - :Section(id) -{ +CommandSection::CommandSection(int id, const char* command, ...) : Section(id) { va_list args; va_start(args, command); init(command, args); va_end(args); } -CommandSection::~CommandSection() -{ - free(mCommand); -} +CommandSection::~CommandSection() { free(mCommand); } -status_t -CommandSection::Execute(ReportRequestSet* requests) const -{ +status_t CommandSection::Execute(ReportRequestSet* requests) const { FdBuffer buffer; Fpipe cmdPipe; Fpipe ihPipe; @@ -537,13 +515,15 @@ CommandSection::Execute(ReportRequestSet* requests) const if (cmdPid == 0) { // replace command's stdout with ihPipe's write Fd if (dup2(cmdPipe.writeFd(), STDOUT_FILENO) != 1 || !ihPipe.close() || !cmdPipe.close()) { - ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno)); + ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), + strerror(errno)); _exit(EXIT_FAILURE); } - execvp(this->mCommand[0], (char *const *) this->mCommand); - int err = errno; // record command error code - ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno)); - _exit(err); // exit with command error code + execvp(this->mCommand[0], (char* const*)this->mCommand); + int err = errno; // record command error code + ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), + strerror(errno)); + _exit(err); // exit with command error code } pid_t ihPid = fork_execute_incident_helper(this->id, this->name.string(), cmdPipe, ihPipe); if (ihPid == -1) { @@ -555,24 +535,26 @@ CommandSection::Execute(ReportRequestSet* requests) const status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); kill_child(cmdPid); kill_child(ihPid); return readStatus; } - // TODO: wait for command here has one trade-off: the failed status of command won't be detected until + // TODO: wait for command here has one trade-off: the failed status of command won't be detected + // until // buffer timeout, but it has advatage on starting the data stream earlier. status_t cmdStatus = wait_child(cmdPid); - status_t ihStatus = wait_child(ihPid); + status_t ihStatus = wait_child(ihPid); if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) { - ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s", - this->name.string(), strerror(-cmdStatus), strerror(-ihStatus)); + ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident " + "helper: %s", + this->name.string(), strerror(-cmdStatus), strerror(-ihStatus)); return cmdStatus != NO_ERROR ? cmdStatus : ihStatus; } - ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), - (int)buffer.durationMs()); + VLOG("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(), + (int)buffer.durationMs()); status_t err = write_report_requests(this->id, buffer, requests); if (err != NO_ERROR) { ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err)); @@ -583,9 +565,7 @@ CommandSection::Execute(ReportRequestSet* requests) const // ================================================================================ DumpsysSection::DumpsysSection(int id, const char* service, ...) - :WorkerThreadSection(id), - mService(service) -{ + : WorkerThreadSection(id), mService(service) { name = "dumpsys "; name += service; @@ -603,13 +583,9 @@ DumpsysSection::DumpsysSection(int id, const char* service, ...) va_end(args); } -DumpsysSection::~DumpsysSection() -{ -} +DumpsysSection::~DumpsysSection() {} -status_t -DumpsysSection::BlockingCall(int pipeWriteFd) const -{ +status_t DumpsysSection::BlockingCall(int pipeWriteFd) const { // checkService won't wait for the service to show up like getService will. sp<IBinder> service = defaultServiceManager()->checkService(mService); @@ -633,30 +609,23 @@ DumpsysSection::BlockingCall(int pipeWriteFd) const // initialization only once in Section.cpp. map<log_id_t, log_time> LogSection::gLastLogsRetrieved; -LogSection::LogSection(int id, log_id_t logID) - :WorkerThreadSection(id), - mLogID(logID) -{ +LogSection::LogSection(int id, log_id_t logID) : WorkerThreadSection(id), mLogID(logID) { name += "logcat "; name += android_log_id_to_name(logID); switch (logID) { - case LOG_ID_EVENTS: - case LOG_ID_STATS: - case LOG_ID_SECURITY: - mBinary = true; - break; - default: - mBinary = false; + case LOG_ID_EVENTS: + case LOG_ID_STATS: + case LOG_ID_SECURITY: + mBinary = true; + break; + default: + mBinary = false; } } -LogSection::~LogSection() -{ -} +LogSection::~LogSection() {} -static size_t -trimTail(char const* buf, size_t len) -{ +static size_t trimTail(char const* buf, size_t len) { while (len > 0) { char c = buf[len - 1]; if (c == '\0' || c == ' ' || c == '\n' || c == '\r' || c == ':') { @@ -672,17 +641,15 @@ static inline int32_t get4LE(uint8_t const* src) { return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); } -status_t -LogSection::BlockingCall(int pipeWriteFd) const -{ +status_t LogSection::BlockingCall(int pipeWriteFd) const { status_t err = NO_ERROR; // Open log buffer and getting logs since last retrieved time if any. unique_ptr<logger_list, void (*)(logger_list*)> loggers( - gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() ? - android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) : - android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, - gLastLogsRetrieved[mLogID], 0), - android_logger_list_free); + gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() + ? android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) + : android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, + gLastLogsRetrieved[mLogID], 0), + android_logger_list_free); if (android_logger_open(loggers.get(), mLogID) == NULL) { ALOGW("LogSection %s: Can't get logger.", this->name.string()); @@ -693,7 +660,7 @@ LogSection::BlockingCall(int pipeWriteFd) const log_time lastTimestamp(0); ProtoOutputStream proto; - while (true) { // keeps reading until logd buffer is fully read. + while (true) { // keeps reading until logd buffer is fully read. status_t err = android_logger_list_read(loggers.get(), &msg); // err = 0 - no content, unexpected connection drop or EOF. // err = +ive number - size of retrieved data from logger @@ -708,7 +675,8 @@ LogSection::BlockingCall(int pipeWriteFd) const if (mBinary) { // remove the first uint32 which is tag's index in event log tags android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t), - msg.len() - sizeof(uint32_t));; + msg.len() - sizeof(uint32_t)); + ; android_log_list_element elem; lastTimestamp.tv_sec = msg.entry_v1.sec; @@ -718,38 +686,46 @@ LogSection::BlockingCall(int pipeWriteFd) const long long token = proto.start(LogProto::BINARY_LOGS); proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec); proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec); - proto.write(BinaryLogEntry::UID, (int) msg.entry_v4.uid); + proto.write(BinaryLogEntry::UID, (int)msg.entry_v4.uid); proto.write(BinaryLogEntry::PID, msg.entry_v1.pid); proto.write(BinaryLogEntry::TID, msg.entry_v1.tid); - proto.write(BinaryLogEntry::TAG_INDEX, get4LE(reinterpret_cast<uint8_t const*>(msg.msg()))); + proto.write(BinaryLogEntry::TAG_INDEX, + get4LE(reinterpret_cast<uint8_t const*>(msg.msg()))); do { elem = android_log_read_next(context); long long elemToken = proto.start(BinaryLogEntry::ELEMS); switch (elem.type) { case EVENT_TYPE_INT: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_INT); - proto.write(BinaryLogEntry::Elem::VAL_INT32, (int) elem.data.int32); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_INT); + proto.write(BinaryLogEntry::Elem::VAL_INT32, (int)elem.data.int32); break; case EVENT_TYPE_LONG: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LONG); - proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long) elem.data.int64); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_LONG); + proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long)elem.data.int64); break; case EVENT_TYPE_STRING: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_STRING); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_STRING); proto.write(BinaryLogEntry::Elem::VAL_STRING, elem.data.string, elem.len); break; case EVENT_TYPE_FLOAT: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_FLOAT); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_FLOAT); proto.write(BinaryLogEntry::Elem::VAL_FLOAT, elem.data.float32); break; case EVENT_TYPE_LIST: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_LIST); break; case EVENT_TYPE_LIST_STOP: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP); break; case EVENT_TYPE_UNKNOWN: - proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN); + proto.write(BinaryLogEntry::Elem::TYPE, + BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN); break; } proto.end(elemToken); @@ -777,7 +753,8 @@ LogSection::BlockingCall(int pipeWriteFd) const proto.write(TextLogEntry::PID, entry.pid); proto.write(TextLogEntry::TID, entry.tid); proto.write(TextLogEntry::TAG, entry.tag, trimTail(entry.tag, entry.tagLen)); - proto.write(TextLogEntry::LOG, entry.message, trimTail(entry.message, entry.messageLen)); + proto.write(TextLogEntry::LOG, entry.message, + trimTail(entry.message, entry.messageLen)); proto.end(token); } } diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index d440ee92601c..d6446810c40f 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -13,31 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef SECTIONS_H #define SECTIONS_H #include "Reporter.h" -#include <map> #include <stdarg.h> +#include <map> -#include <utils/String8.h> #include <utils/String16.h> +#include <utils/String8.h> #include <utils/Vector.h> using namespace android; -const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds +const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds /** * Base class for sections */ -class Section -{ +class Section { public: const int id; - const int64_t timeoutMs; // each section must have a timeout + const int64_t timeoutMs; // each section must have a timeout String8 name; Section(int id, const int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS); @@ -49,8 +49,7 @@ public: /** * Section that generates incident headers. */ -class HeaderSection : public Section -{ +class HeaderSection : public Section { public: HeaderSection(); virtual ~HeaderSection(); @@ -59,10 +58,20 @@ public: }; /** + * Section that generates incident metadata. + */ +class MetadataSection : public Section { +public: + MetadataSection(); + virtual ~MetadataSection(); + + virtual status_t Execute(ReportRequestSet* requests) const; +}; + +/** * Section that reads in a file. */ -class FileSection : public Section -{ +class FileSection : public Section { public: FileSection(int id, const char* filename, const int64_t timeoutMs = 5000 /* 5 seconds */); virtual ~FileSection(); @@ -71,14 +80,13 @@ public: private: const char* mFilename; - bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately + bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately }; /** * Base class for sections that call a command that might need a timeout. */ -class WorkerThreadSection : public Section -{ +class WorkerThreadSection : public Section { public: WorkerThreadSection(int id); virtual ~WorkerThreadSection(); @@ -91,8 +99,7 @@ public: /** * Section that forks and execs a command, and puts stdout as the section. */ -class CommandSection : public Section -{ +class CommandSection : public Section { public: CommandSection(int id, const int64_t timeoutMs, const char* command, ...); @@ -111,8 +118,7 @@ private: /** * Section that calls dumpsys on a system service. */ -class DumpsysSection : public WorkerThreadSection -{ +class DumpsysSection : public WorkerThreadSection { public: DumpsysSection(int id, const char* service, ...); virtual ~DumpsysSection(); @@ -127,8 +133,7 @@ private: /** * Section that reads from logd. */ -class LogSection : public WorkerThreadSection -{ +class LogSection : public WorkerThreadSection { // global last log retrieved timestamp for each log_id_t. static map<log_id_t, log_time> gLastLogsRetrieved; @@ -143,5 +148,4 @@ private: bool mBinary; }; -#endif // SECTIONS_H - +#endif // SECTIONS_H diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp new file mode 100644 index 000000000000..2415860572fb --- /dev/null +++ b/cmds/incidentd/src/incidentd_util.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "incidentd_util.h" + +#include "section_list.h" + +const Privacy* get_privacy_of_section(int id) { + int l = 0; + int r = PRIVACY_POLICY_COUNT - 1; + while (l <= r) { + int mid = (l + r) >> 1; + const Privacy* p = PRIVACY_POLICY_LIST[mid]; + + if (p->field_id < (uint32_t)id) { + l = mid + 1; + } else if (p->field_id > (uint32_t)id) { + r = mid - 1; + } else { + return p; + } + } + return NULL; +} + +// ================================================================================ +Fpipe::Fpipe() : mRead(), mWrite() {} + +Fpipe::~Fpipe() { close(); } + +bool Fpipe::close() { + mRead.reset(); + mWrite.reset(); + return true; +} + +bool Fpipe::init() { return Pipe(&mRead, &mWrite); } + +int Fpipe::readFd() const { return mRead.get(); } + +int Fpipe::writeFd() const { return mWrite.get(); }
\ No newline at end of file diff --git a/cmds/incidentd/src/io_util.h b/cmds/incidentd/src/incidentd_util.h index 320dd6c386d2..09aa0404277a 100644 --- a/cmds/incidentd/src/io_util.h +++ b/cmds/incidentd/src/incidentd_util.h @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once -#ifndef IO_UTIL_H -#define IO_UTIL_H +#ifndef INCIDENTD_UTIL_H +#define INCIDENTD_UTIL_H -#include <stdint.h> -#include <utils/Errors.h> +#include <android-base/unique_fd.h> -using namespace android; +#include "Privacy.h" -status_t write_all(int fd, uint8_t const* buf, size_t size); +using namespace android::base; + +const Privacy* get_privacy_of_section(int id); class Fpipe { public: @@ -35,7 +37,8 @@ public: int writeFd() const; private: - int mFds[2]; + unique_fd mRead; + unique_fd mWrite; }; -#endif // IO_UTIL_H
\ No newline at end of file +#endif // INCIDENTD_UTIL_H
\ No newline at end of file diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp index 3a7511d43048..38b7449403fe 100644 --- a/cmds/incidentd/src/main.cpp +++ b/cmds/incidentd/src/main.cpp @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "IncidentService.h" @@ -23,25 +22,22 @@ #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <binder/Status.h> -#include <cutils/log.h> #include <utils/Looper.h> #include <utils/StrongPointer.h> -#include <sys/types.h> #include <sys/stat.h> +#include <sys/types.h> using namespace android; // ================================================================================ -int -main(int /*argc*/, char** /*argv*/) -{ +int main(int /*argc*/, char** /*argv*/) { // Set up the looper sp<Looper> looper(Looper::prepare(0 /* opts */)); // Set up the binder sp<ProcessState> ps(ProcessState::self()); - ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram + ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram ps->startThreadPool(); ps->giveThreadPoolName(); IPCThreadState::self()->disableBackgroundScheduling(true); diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp index 20111d8ae89a..b71c066201c4 100644 --- a/cmds/incidentd/src/report_directory.cpp +++ b/cmds/incidentd/src/report_directory.cpp @@ -13,19 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#define LOG_TAG "incidentd" +#include "Log.h" #include "report_directory.h" -#include <cutils/log.h> #include <private/android_filesystem_config.h> #include <utils/String8.h> -#include <sys/types.h> -#include <sys/stat.h> #include <dirent.h> #include <libgen.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #include <vector> @@ -33,9 +31,7 @@ using namespace android; using namespace std; -status_t -create_directory(const char* directory) -{ +status_t create_directory(const char* directory) { struct stat st; status_t err = NO_ERROR; char* dir = strdup(directory); @@ -75,14 +71,14 @@ create_directory(const char* directory) goto done; } if ((st.st_mode & 0777) != 0770) { - ALOGE("No incident reports today. Mode is %0o on report directory %s", - st.st_mode, directory); + ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode, + directory); err = BAD_VALUE; goto done; } if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) { ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", - st.st_uid, st.st_gid, directory); + st.st_uid, st.st_gid, directory); err = BAD_VALUE; goto done; } @@ -92,20 +88,17 @@ done: return err; } -static bool -stat_mtime_cmp(const pair<String8,struct stat>& a, const pair<String8,struct stat>& b) -{ +static bool stat_mtime_cmp(const pair<String8, struct stat>& a, + const pair<String8, struct stat>& b) { return a.second.st_mtime < b.second.st_mtime; } -void -clean_directory(const char* directory, off_t maxSize, size_t maxCount) -{ +void clean_directory(const char* directory, off_t maxSize, size_t maxCount) { DIR* dir; struct dirent* entry; struct stat st; - vector<pair<String8,struct stat>> files; + vector<pair<String8, struct stat>> files; if ((dir = opendir(directory)) == NULL) { ALOGE("Couldn't open incident directory: %s", directory); @@ -131,7 +124,7 @@ clean_directory(const char* directory, off_t maxSize, size_t maxCount) if (!S_ISREG(st.st_mode)) { continue; } - files.push_back(pair<String8,struct stat>(filename, st)); + files.push_back(pair<String8, struct stat>(filename, st)); totalSize += st.st_size; totalCount++; @@ -148,8 +141,8 @@ clean_directory(const char* directory, off_t maxSize, size_t maxCount) sort(files.begin(), files.end(), stat_mtime_cmp); // Remove files until we're under our limits. - for (vector<pair<String8,struct stat>>::iterator it = files.begin(); - it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) { + for (vector<pair<String8, struct stat>>::iterator it = files.begin(); + it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) { remove(it->first.string()); totalSize -= it->second.st_size; totalCount--; diff --git a/cmds/incidentd/src/report_directory.h b/cmds/incidentd/src/report_directory.h index bed4f869cfe4..2a3cf4c0f701 100644 --- a/cmds/incidentd/src/report_directory.h +++ b/cmds/incidentd/src/report_directory.h @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef DIRECTORY_CLEANER_H #define DIRECTORY_CLEANER_H -#include <utils/Errors.h> - #include <sys/types.h> +#include <utils/Errors.h> -using namespace android; - -status_t create_directory(const char* directory); +android::status_t create_directory(const char* directory); void clean_directory(const char* directory, off_t maxSize, size_t maxCount); -#endif // DIRECTORY_CLEANER_H +#endif // DIRECTORY_CLEANER_H diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h index ddc0505df331..697e66f9cf40 100644 --- a/cmds/incidentd/src/section_list.h +++ b/cmds/incidentd/src/section_list.h @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #ifndef SECTION_LIST_H #define SECTION_LIST_H -#include <log/log_event_list.h> // include log_id_t enums. +#include <log/log_event_list.h> // include log_id_t enums. #include "Privacy.h" #include "Section.h" @@ -36,5 +37,4 @@ extern const Privacy** PRIVACY_POLICY_LIST; extern const int PRIVACY_POLICY_COUNT; -#endif // SECTION_LIST_H - +#endif // SECTION_LIST_H diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp index 3fd2ed82a26e..956c8d39346a 100644 --- a/cmds/incidentd/tests/FdBuffer_test.cpp +++ b/cmds/incidentd/tests/FdBuffer_test.cpp @@ -11,11 +11,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -#define LOG_TAG "incidentd" +#include "Log.h" #include "FdBuffer.h" -#include "io_util.h" +#include "incidentd_util.h" #include <android-base/file.h> #include <android-base/test_utils.h> @@ -48,7 +47,7 @@ public: } void AssertBufferContent(const char* expected) { - int i=0; + int i = 0; EncodedBuffer::iterator it = buffer.data(); while (it.hasNext()) { ASSERT_EQ(it.next(), expected[i++]); @@ -100,7 +99,7 @@ TEST_F(FdBufferTest, ReadAndIterate) { ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); - int i=0; + int i = 0; EncodedBuffer::iterator it = buffer.data(); while (it.hasNext()) { EXPECT_EQ(it.next(), (uint8_t)testdata[i++]); @@ -118,7 +117,7 @@ TEST_F(FdBufferTest, ReadTimeout) { if (pid == 0) { close(c2pPipe.readFd()); - while(true) { + while (true) { write(c2pPipe.writeFd(), "poo", 3); sleep(1); } @@ -130,7 +129,7 @@ TEST_F(FdBufferTest, ReadTimeout) { ASSERT_EQ(NO_ERROR, status); EXPECT_TRUE(buffer.timedOut()); - kill(pid, SIGKILL); // reap the child process + kill(pid, SIGKILL); // reap the child process } } @@ -155,8 +154,8 @@ TEST_F(FdBufferTest, ReadInStreamAndWrite) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); AssertBufferReadSuccessful(HEAD.size() + testdata.size()); AssertBufferContent(expected.c_str()); wait(&pid); @@ -187,8 +186,8 @@ TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); AssertBufferReadSuccessful(HEAD.size() + testdata.size()); AssertBufferContent(expected.c_str()); wait(&pid); @@ -212,8 +211,8 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); AssertBufferReadSuccessful(0); AssertBufferContent(""); wait(&pid); @@ -222,7 +221,7 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { const std::string testFile = kTestDataPath + "morethan4MB.txt"; - size_t fourMB = (size_t) 4 * 1024 * 1024; + size_t fourMB = (size_t)4 * 1024 * 1024; int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC); ASSERT_NE(fd, -1); int pid = fork(); @@ -239,8 +238,8 @@ TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd, - p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), + c2pPipe.readFd(), READ_TIMEOUT)); EXPECT_EQ(buffer.size(), fourMB); EXPECT_FALSE(buffer.timedOut()); EXPECT_TRUE(buffer.truncated()); @@ -276,9 +275,9 @@ TEST_F(FdBufferTest, ReadInStreamTimeOut) { close(p2cPipe.readFd()); close(c2pPipe.writeFd()); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, - p2cPipe.writeFd(), c2pPipe.readFd(), QUICK_TIMEOUT_MS)); + ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd, p2cPipe.writeFd(), + c2pPipe.readFd(), QUICK_TIMEOUT_MS)); EXPECT_TRUE(buffer.timedOut()); - kill(pid, SIGKILL); // reap the child process + kill(pid, SIGKILL); // reap the child process } } diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp index c7bfe5555743..7ea9bbfcd8c7 100644 --- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp +++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp @@ -11,8 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -#define LOG_TAG "incidentd" +#include "Log.h" #include "FdBuffer.h" #include "PrivacyBuffer.h" @@ -37,13 +36,12 @@ const uint8_t OTHER_TYPE = 1; const uint8_t STRING_TYPE = 9; const uint8_t MESSAGE_TYPE = 11; const string STRING_FIELD_0 = "\x02\viamtestdata"; -const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 +const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 const string STRING_FIELD_2 = "\x12\vwhatthefuck"; -const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 -const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 +const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 +const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2; - class PrivacyBufferTest : public Test { public: virtual ~PrivacyBufferTest() { @@ -55,9 +53,7 @@ public: } } - virtual void SetUp() override { - ASSERT_NE(tf.fd, -1); - } + virtual void SetUp() override { ASSERT_NE(tf.fd, -1); } void writeToFdBuffer(string str) { ASSERT_TRUE(WriteStringToFile(str, tf.path)); @@ -81,11 +77,11 @@ public: } void assertStripByFields(uint8_t dest, string expected, int size, Privacy* privacy, ...) { - Privacy* list[size+1]; + Privacy* list[size + 1]; list[0] = privacy; va_list args; va_start(args, privacy); - for (int i=1; i<size; i++) { + for (int i = 1; i < size; i++) { Privacy* p = va_arg(args, Privacy*); list[i] = p; } @@ -115,13 +111,14 @@ public: } FdBuffer buffer; + private: TemporaryFile tf; // Littering this code with unique_ptr (or similar) is ugly, so we just // mass-free everything after the test completes. - std::vector<Privacy *> privacies; + std::vector<Privacy*> privacies; - Privacy *new_uninit_privacy() { + Privacy* new_uninit_privacy() { Privacy* p = new Privacy; privacies.push_back(p); return p; @@ -165,63 +162,69 @@ TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) { TEST_F(PrivacyBufferTest, NoStripVarintField) { writeToFdBuffer(VARINT_FIELD_1); - assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, + create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) { writeToFdBuffer(STRING_FIELD_2); - assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, + create_privacy(2, STRING_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripFixed64Field) { writeToFdBuffer(FIX64_FIELD_3); - assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, + create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripFixed32Field) { writeToFdBuffer(FIX32_FIELD_4); - assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, + create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) { writeToFdBuffer(MESSAGE_FIELD_5); - assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC)); + assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, + create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC)); } TEST_F(PrivacyBufferTest, StripVarintAndString) { - writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 - + FIX64_FIELD_3 + FIX32_FIELD_4); + writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + + FIX32_FIELD_4); string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4; - assertStripByFields(DEST_EXPLICIT, expected, 2, - create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(2, STRING_TYPE, DEST_LOCAL)); + assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL), + create_privacy(2, STRING_TYPE, DEST_LOCAL)); } TEST_F(PrivacyBufferTest, StripVarintAndFixed64) { - writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 - + FIX64_FIELD_3 + FIX32_FIELD_4); + writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + + FIX32_FIELD_4); string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4; - assertStripByFields(DEST_EXPLICIT, expected, 2, - create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(3, OTHER_TYPE, DEST_LOCAL)); + assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL), + create_privacy(3, OTHER_TYPE, DEST_LOCAL)); } TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) { writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; assertStripByFields(DEST_EXPLICIT, expected, 1, create_message_privacy(5, list)); } TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) { writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; - assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), create_message_privacy(5, list)); + assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), + create_message_privacy(5, list)); } TEST_F(PrivacyBufferTest, ClearAndStrip) { string data = STRING_FIELD_0 + VARINT_FIELD_1; writeToFdBuffer(data); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; EncodedBuffer::iterator bufData = buffer.data(); PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData); PrivacySpec spec1 = PrivacySpec::new_spec(DEST_EXPLICIT); @@ -235,7 +238,7 @@ TEST_F(PrivacyBufferTest, ClearAndStrip) { TEST_F(PrivacyBufferTest, BadDataInFdBuffer) { writeToFdBuffer("iambaddata"); - Privacy* list[] = { create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL }; + Privacy* list[] = {create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL}; EncodedBuffer::iterator bufData = buffer.data(); PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData); PrivacySpec spec; @@ -244,8 +247,8 @@ TEST_F(PrivacyBufferTest, BadDataInFdBuffer) { TEST_F(PrivacyBufferTest, BadDataInNestedMessage) { writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe"); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; - Privacy* field5[] = { create_message_privacy(5, list), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; + Privacy* field5[] = {create_message_privacy(5, list), NULL}; EncodedBuffer::iterator bufData = buffer.data(); PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData); PrivacySpec spec; @@ -256,7 +259,7 @@ TEST_F(PrivacyBufferTest, SelfRecursionMessage) { string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5; writeToFdBuffer(input); Privacy* field5 = create_message_privacy(5, NULL); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL}; field5->children = list; string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2; assertStrip(DEST_EXPLICIT, expected, field5); @@ -264,7 +267,7 @@ TEST_F(PrivacyBufferTest, SelfRecursionMessage) { TEST_F(PrivacyBufferTest, AutoMessage) { writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5); - Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL }; + Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC); autoMsg->children = list; string expected = "\x2a\xd" + STRING_FIELD_2; diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index c494bd646b0b..bd359ac9516c 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -11,8 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -#define LOG_TAG "incidentd" +#include "Log.h" #include "Reporter.h" @@ -26,7 +25,6 @@ #include <gtest/gtest.h> #include <string.h> - using namespace android; using namespace android::base; using namespace android::binder; @@ -35,8 +33,7 @@ using namespace std; using ::testing::StrEq; using ::testing::Test; -class TestListener : public IIncidentReportStatusListener -{ +class TestListener : public IIncidentReportStatusListener { public: int startInvoked; int finishInvoked; @@ -44,8 +41,8 @@ public: map<int, int> startSections; map<int, int> finishSections; - TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0) {}; - virtual ~TestListener() {}; + TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0){}; + virtual ~TestListener(){}; virtual Status onReportStarted() { startInvoked++; @@ -53,16 +50,14 @@ public: }; virtual Status onReportSectionStatus(int section, int status) { switch (status) { - case IIncidentReportStatusListener::STATUS_STARTING: - if (startSections.count(section) == 0) - startSections[section] = 0; - startSections[section] = startSections[section] + 1; - break; - case IIncidentReportStatusListener::STATUS_FINISHED: - if (finishSections.count(section) == 0) - finishSections[section] = 0; - finishSections[section] = finishSections[section] + 1; - break; + case IIncidentReportStatusListener::STATUS_STARTING: + if (startSections.count(section) == 0) startSections[section] = 0; + startSections[section] = startSections[section] + 1; + break; + case IIncidentReportStatusListener::STATUS_FINISHED: + if (finishSections.count(section) == 0) finishSections[section] = 0; + finishSections[section] = finishSections[section] + 1; + break; } return Status::ok(); }; @@ -156,7 +151,8 @@ TEST_F(ReporterTest, RunReportWithHeaders) { string result; ReadFileToString(tf.path, &result); - EXPECT_THAT(result, StrEq("\n\x2" "\b\f")); + EXPECT_THAT(result, StrEq("\n\x2" + "\b\f")); EXPECT_EQ(l->startInvoked, 2); EXPECT_EQ(l->finishInvoked, 2); @@ -178,5 +174,24 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); vector<string> results = InspectFiles(); ASSERT_EQ((int)results.size(), 1); - EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd"); + EXPECT_EQ(results[0], + "\n\x2" + "\b\f\n\x6" + "\x12\x4" + "abcd"); } + +TEST_F(ReporterTest, ReportMetadata) { + IncidentReportArgs args; + args.addSection(1); + args.setDest(android::os::DEST_EXPLICIT); + sp<ReportRequest> r = new ReportRequest(args, l, -1); + reporter->batch.add(r); + + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); + auto metadata = reporter->batch.metadata(); + EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest()); + EXPECT_EQ(1, metadata.request_size()); + EXPECT_TRUE(metadata.use_dropbox()); + EXPECT_EQ(0, metadata.sections_size()); +}
\ No newline at end of file diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 2cfd7df6be84..a1f4fdc77300 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -11,13 +11,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -#define LOG_TAG "incidentd" +#include "Log.h" #include "Section.h" #include <android-base/file.h> #include <android-base/test_utils.h> +#include <android/os/IncidentReportArgs.h> #include <frameworks/base/libs/incident/proto/android/os/header.pb.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -29,9 +29,9 @@ const int REVERSE_PARSER = 1; const int QUICK_TIMEOUT_MS = 100; -const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 +const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 const string STRING_FIELD_2 = "\x12\vwhatthefuck"; -const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 +const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 using namespace android::base; using namespace android::binder; @@ -43,11 +43,10 @@ using ::testing::internal::GetCapturedStdout; // NOTICE: this test requires /system/bin/incident_helper is installed. -class SimpleListener : public IIncidentReportStatusListener -{ +class SimpleListener : public IIncidentReportStatusListener { public: - SimpleListener() {}; - virtual ~SimpleListener() {}; + SimpleListener(){}; + virtual ~SimpleListener(){}; virtual Status onReportStarted() { return Status::ok(); }; virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); }; @@ -83,12 +82,26 @@ TEST(SectionTest, HeaderSection) { string content; CaptureStdout(); ASSERT_EQ(NO_ERROR, hs.Execute(&requests)); - EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup")); + EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" + "\x12\x3" + "axe\n\x05\x12\x03pup")); EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); } +TEST(SectionTest, MetadataSection) { + MetadataSection ms; + ReportRequestSet requests; + + requests.setMainFd(STDOUT_FILENO); + requests.sectionStats(1)->set_success(true); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, ms.Execute(&requests)); + EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1")); +} + TEST(SectionTest, FileSection) { TemporaryFile tf; FileSection fs(REVERSE_PARSER, tf.path); @@ -243,8 +256,9 @@ TEST(SectionTest, TestMultipleRequests) { IncidentReportArgs args1, args2, args3; args1.setAll(true); - args1.setDest(0); // LOCAL - args2.setAll(true); // default to explicit + args1.setDest(android::os::DEST_LOCAL); + args2.setAll(true); + args2.setDest(android::os::DEST_EXPLICIT); sp<SimpleListener> l = new SimpleListener(); requests.add(new ReportRequest(args1, l, output1.fd)); requests.add(new ReportRequest(args2, l, output2.fd)); @@ -257,12 +271,12 @@ TEST(SectionTest, TestMultipleRequests) { string content, expect; expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3; - char c = (char) expect.size(); + char c = (char)expect.size(); EXPECT_TRUE(ReadFileToString(output1.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); expect = STRING_FIELD_2 + FIX64_FIELD_3; - c = (char) expect.size(); + c = (char)expect.size(); EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); @@ -283,10 +297,12 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); - IncidentReportArgs args1, args2, args3, args4; + IncidentReportArgs args1, args2, args3; args1.setAll(true); + args1.setDest(android::os::DEST_EXPLICIT); args2.setAll(true); - args4.setAll(true); + args2.setDest(android::os::DEST_EXPLICIT); + args3.setAll(true); sp<SimpleListener> l = new SimpleListener(); requests.add(new ReportRequest(args1, l, output1.fd)); requests.add(new ReportRequest(args2, l, output2.fd)); @@ -299,7 +315,7 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { string content, expect; expect = STRING_FIELD_2 + FIX64_FIELD_3; - char c = (char) expect.size(); + char c = (char)expect.size(); // output1 and output2 are the same EXPECT_TRUE(ReadFileToString(output1.path, &content)); @@ -307,7 +323,8 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); - // because args3 doesn't set section, so it should receive nothing + // output3 has only auto field + c = (char)STRING_FIELD_2.size(); EXPECT_TRUE(ReadFileToString(output3.path, &content)); - EXPECT_THAT(content, StrEq("")); + EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2)); }
\ No newline at end of file diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp index 1d6213fd19b3..bd2d15cc38be 100644 --- a/cmds/incidentd/tests/section_list.cpp +++ b/cmds/incidentd/tests/section_list.cpp @@ -1,25 +1,17 @@ // This file is a dummy section_list.cpp used for test only. #include "section_list.h" -const Section* SECTION_LIST[] = { - NULL -}; +const Section* SECTION_LIST[] = {NULL}; -Privacy sub_field_1 { 1, 1, NULL, DEST_LOCAL, NULL }; -Privacy sub_field_2 { 2, 9, NULL, DEST_AUTOMATIC, NULL }; +Privacy sub_field_1{1, 1, NULL, DEST_LOCAL, NULL}; +Privacy sub_field_2{2, 9, NULL, DEST_AUTOMATIC, NULL}; -Privacy* list[] = { - &sub_field_1, - &sub_field_2, - NULL }; +Privacy* list[] = {&sub_field_1, &sub_field_2, NULL}; -Privacy field_0 { 0, 11, list, DEST_EXPLICIT, NULL }; -Privacy field_1 { 1, 9, NULL, DEST_AUTOMATIC, NULL }; +Privacy field_0{0, 11, list, DEST_EXPLICIT, NULL}; +Privacy field_1{1, 9, NULL, DEST_AUTOMATIC, NULL}; -Privacy* final_list[] = { - &field_0, - &field_1 -}; +Privacy* final_list[] = {&field_0, &field_1}; const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(final_list); diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS new file mode 100644 index 000000000000..362d411d03a5 --- /dev/null +++ b/cmds/statsd/OWNERS @@ -0,0 +1,6 @@ +bookatz@google.com +jinyithu@google.com +kwekua@google.com +stlafon@google.com +yaochen@google.com +yanglu@google.com diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 0a4e412bff20..791fb14a7717 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -571,8 +571,8 @@ status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& good = true; } else { fprintf(out, - "Selecting a UID for writing Appbreadcrumb can only be dumped for other UIDs on eng" - " or userdebug builds.\n"); + "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs " + "on eng or userdebug builds.\n"); } } if (good) { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index fd658690435e..9690de702c24 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -193,8 +193,8 @@ private: status_t cmd_write_data_to_disk(FILE* out); /** - * Write an AppBreadcrumbReported event to the StatsLog buffer, as though StatsLog.write - * (APP_BREADCRUMB_REPORTED). + * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling + * StatsLog.write(APP_BREADCRUMB_REPORTED). */ status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1224504c6703..85e209be6413 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -1471,7 +1471,7 @@ message SystemUptime { */ message CpuActiveTime { optional uint64 uid = 1; - optional uint32 cluster_number =2; + optional uint32 cluster_number = 2; optional uint64 idx = 3; optional uint64 time_millis = 4; } diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index f254327fcc16..7baa5e57679e 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -42,6 +42,7 @@ public: const static int kDimensionKeySizeHardLimit = 500; const static int kMaxConfigCount = 10; + const static int kMaxAlertCountPerConfig = 100; const static int kMaxConditionCountPerConfig = 200; const static int kMaxMetricCountPerConfig = 300; const static int kMaxMatcherCountPerConfig = 500; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 574c59f740bd..8663e5eed7a7 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -123,6 +123,7 @@ public: return byteSizeLocked(); } + /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) { std::lock_guard<std::mutex> lock(mMutex); sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey); diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 66e1aeb8f43e..e75b710cc9db 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -92,8 +92,10 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, ALOGE("This config is too big! Reject!"); mConfigValid = false; } - - // TODO: add alert size. + if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { + ALOGE("This config has too many alerts! Reject!"); + mConfigValid = false; + } // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(), mAllConditionTrackers.size(), @@ -188,8 +190,9 @@ void MetricsManager::onLogEvent(const LogEvent& event) { return; } - if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { // Check that app hook fields are valid. - // TODO: Find a way to make these checks easier to maintain if the app hooks get changed. + if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { + // Check that app breadcrumb reported fields are valid. + // TODO: Find a way to make these checks easier to maintain. status_t err = NO_ERROR; // Uid is 3rd from last field and must match the caller's uid, diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 769f46dbd7e6..71e5c33b1b88 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -513,10 +513,12 @@ bool initAlerts(const StatsdConfig& config, const int metricIndex = itr->second; sp<MetricProducer> metric = allMetricProducers[metricIndex]; sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert); - if (anomalyTracker != nullptr) { - anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); - allAnomalyTrackers.push_back(anomalyTracker); + if (anomalyTracker == nullptr) { + // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). + return false; } + anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); + allAnomalyTrackers.push_back(anomalyTracker); } for (int i = 0; i < config.subscription_size(); ++i) { const Subscription& subscription = config.subscription(i); diff --git a/core/java/android/annotation/OWNERS b/core/java/android/annotation/OWNERS new file mode 100644 index 000000000000..d6bb71b50e34 --- /dev/null +++ b/core/java/android/annotation/OWNERS @@ -0,0 +1 @@ +tnorbye@google.com diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 6c3d248c820f..8a9efe876321 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,7 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; + import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -135,6 +136,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1898,7 +1900,7 @@ public class Activity extends ContextThemeWrapper if (isFinishing()) { if (mAutoFillResetNeeded) { - getAutofillManager().onActivityFinished(); + getAutofillManager().onActivityFinishing(); } else if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) { // Activity was launched when user tapped a link in the Autofill Save UI - since @@ -7689,6 +7691,9 @@ public class Activity extends ContextThemeWrapper } } } + if (android.view.autofill.Helper.sVerbose) { + Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible)); + } return visible; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 4c9fb74c9c80..1026550b9b6b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -543,7 +543,6 @@ public class AppOpsManager { OP_CAMERA, // Body sensors OP_BODY_SENSORS, - OP_REQUEST_DELETE_PACKAGES, // APPOP PERMISSIONS OP_ACCESS_NOTIFICATIONS, diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS new file mode 100644 index 000000000000..1c9a43acfa65 --- /dev/null +++ b/core/java/android/app/backup/OWNERS @@ -0,0 +1,7 @@ +artikz@google.com +brufino@google.com +bryanmawhinney@google.com +ctate@google.com +jorlow@google.com +mkarpinski@google.com + diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index a788989a7578..b072ee622ad9 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1000,7 +1000,7 @@ public class ContextWrapper extends Context { */ @Override public boolean isAutofillCompatibilityEnabled() { - return mBase.isAutofillCompatibilityEnabled(); + return mBase != null && mBase.isAutofillCompatibilityEnabled(); } /** @@ -1008,6 +1008,8 @@ public class ContextWrapper extends Context { */ @Override public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { - mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled); + if (mBase != null) { + mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled); + } } } diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index cf0145123b87..424fa833cd48 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -27,9 +27,11 @@ import android.annotation.StyleRes; import android.annotation.StyleableRes; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.Config; +import android.content.res.AssetManager.AssetInputStream; import android.content.res.Configuration.NativeConfig; import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; +import android.graphics.ImageDecoder; import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -752,6 +754,26 @@ public class ResourcesImpl { } /** + * Loads a Drawable from an encoded image stream, or null. + * + * This call will handle closing ais. + */ + private Drawable decodeImageDrawable(@NonNull AssetInputStream ais, + @NonNull Resources wrapper, @NonNull TypedValue value) { + ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais, + wrapper, value); + try { + return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (IOException ioe) { + // This is okay. This may be something that ImageDecoder does not + // support, like SVG. + return null; + } + } + + /** * Loads a drawable from XML or resources stream. */ @NonNull @@ -811,8 +833,8 @@ public class ResourcesImpl { } else { final InputStream is = mAssets.openNonAsset( value.assetCookie, file, AssetManager.ACCESS_STREAMING); - dr = Drawable.createFromResourceStream(wrapper, value, is, file, null); - is.close(); + AssetInputStream ais = (AssetInputStream) is; + dr = decodeImageDrawable(ais, wrapper, value); } } finally { stack.pop(); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 931b5c913851..f08e1cc24b26 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -242,6 +242,9 @@ public class Camera { /** * Returns the number of physical cameras available on this device. + * The return value of this method might change dynamically if the device + * supports external cameras and an external camera is connected or + * disconnected. * * @return total number of accessible camera devices, or 0 if there are no * cameras or an error was encountered enumerating them. @@ -3542,8 +3545,8 @@ public class Camera { /** * Gets the horizontal angle of view in degrees. * - * @return horizontal angle of view. This method will always return a - * valid value. + * @return horizontal angle of view. Returns -1.0 when the device + * doesn't report view angle information. */ public float getHorizontalViewAngle() { return Float.parseFloat(get(KEY_HORIZONTAL_VIEW_ANGLE)); @@ -3552,8 +3555,8 @@ public class Camera { /** * Gets the vertical angle of view in degrees. * - * @return vertical angle of view. This method will always return a - * valid value. + * @return vertical angle of view. Returns -1.0 when the device + * doesn't report view angle information. */ public float getVerticalViewAngle() { return Float.parseFloat(get(KEY_VERTICAL_VIEW_ANGLE)); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 732f6a519781..e558b7e29a20 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -829,19 +829,24 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> * </ul> * </li> + * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be + * the same.</li> * <li>The logical camera device must be LIMITED or higher device.</li> * </ul> * <p>Both the logical camera device and its underlying physical devices support the * mandatory stream combinations required for their device levels.</p> * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p> * <ul> - * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} + * <li>For each guaranteed stream combination, the logical camera supports replacing one + * logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} * or raw stream with two physical streams of the same size and format, each from a * separate physical camera, given that the size and format are supported by both * physical cameras.</li> - * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't - * advertise RAW capability, but the underlying physical cameras do. This is usually - * the case when the physical cameras have different sensor sizes.</li> + * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical + * cameras do, the logical camera will support guaranteed stream combinations for RAW + * capability, except that the RAW streams will be physical streams, each from a separate + * physical camera. This is usually the case when the physical cameras have different + * sensor sizes.</li> * </ul> * <p>Using physical streams in place of a logical stream of the same size and format will * not slow down the frame rate of the capture, as long as the minimum frame duration diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index b205e2c7649d..a040a09cf469 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -82,11 +82,9 @@ import java.util.List; * * </ul> * - * <p>Please note that surface sharing is currently only enabled for outputs that use the - * {@link ImageFormat#PRIVATE} format. This includes surface sources like - * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder}, - * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using - * the aforementioned format.</p> + * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for + * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE} + * format may be used.</p> * * @see CameraDevice#createCaptureSessionByOutputConfigurations * diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 7dde2ed70698..91c99be32c98 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2662,7 +2662,7 @@ public class ConnectivityManager { * A {@code NetworkCallback} is registered by calling * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)}, - * or {@link #registerDefaultNetworkCallback(NetworkCallback). A {@code NetworkCallback} is + * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is * unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}. * A {@code NetworkCallback} should be registered at most once at any time. * A {@code NetworkCallback} that has been unregistered can be registered again. diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 5d7cf1e3899f..7cd58e8b7c36 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2240,12 +2240,16 @@ public abstract class BatteryStats implements Parcelable { public static final int DATA_CONNECTION_LTE = 13; public static final int DATA_CONNECTION_EHRPD = 14; public static final int DATA_CONNECTION_HSPAP = 15; - public static final int DATA_CONNECTION_OTHER = 16; + public static final int DATA_CONNECTION_GSM = 16; + public static final int DATA_CONNECTION_TD_SCDMA = 17; + public static final int DATA_CONNECTION_IWLAN = 18; + public static final int DATA_CONNECTION_LTE_CA = 19; + public static final int DATA_CONNECTION_OTHER = 20; static final String[] DATA_CONNECTION_NAMES = { "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte", - "ehrpd", "hspap", "other" + "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other" }; public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 630b63f4cba4..c53d005606c4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3692,18 +3692,15 @@ public final class Settings { new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); /** - * User-selected RTT mode + * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT + * calls when supported by the device and carrier. Boolean value. * 0 = OFF - * 1 = FULL - * 2 = VCO - * 3 = HCO - * Uses the same constants as TTY (e.g. {@link android.telecom.TelecomManager#TTY_MODE_OFF}) - * @hide + * 1 = ON */ public static final String RTT_CALLING_MODE = "rtt_calling_mode"; /** @hide */ - public static final Validator RTT_CALLING_MODE_VALIDATOR = TTY_MODE_VALIDATOR; + public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is @@ -12081,6 +12078,28 @@ public final class Settings { "enable_gnss_raw_meas_full_tracking"; /** + * Whether the notification should be ongoing (persistent) when a carrier app install is + * required. + * + * The value is a boolean (1 or 0). + * @hide + */ + @SystemApi + public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = + "install_carrier_app_notification_persistent"; + + /** + * The amount of time (ms) to hide the install carrier app notification after the user has + * ignored it. After this time passes, the notification will be shown again + * + * The value is a long + * @hide + */ + @SystemApi + public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = + "install_carrier_app_notification_sleep_millis"; + + /** * Whether we've enabled zram on this device. Takes effect on * reboot. The value "1" enables zram; "0" disables it, and * everything else is unspecified. diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 1d1333504350..f4dcce1e7e58 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -75,6 +75,7 @@ public final class KeymasterDefs { public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508; + public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509; public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; @@ -216,6 +217,7 @@ public final class KeymasterDefs { public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58; public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59; public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; + public static final int KM_ERROR_DEVICE_LOCKED = -72; public static final int KM_ERROR_UNIMPLEMENTED = -100; public static final int KM_ERROR_VERSION_MISMATCH = -101; public static final int KM_ERROR_UNKNOWN_ERROR = -1000; @@ -262,6 +264,7 @@ public final class KeymasterDefs { sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH, "Invalid MAC or authentication tag length"); sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids"); + sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked"); sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); } diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS index 9f2182eca908..e56137119c28 100644 --- a/core/java/android/text/OWNERS +++ b/core/java/android/text/OWNERS @@ -1,3 +1,5 @@ +set noparent + siyamed@google.com nona@google.com clarabayarri@google.com diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 3fd469630db0..8cb46b704c18 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -23,6 +23,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -396,6 +400,33 @@ public final class PointerIcon implements Parcelable { return true; } + /** + * Get the Bitmap from the Drawable. + * + * If the Bitmap needed to be scaled up to account for density, BitmapDrawable + * handles this at draw time. But this class doesn't actually draw the Bitmap; + * it is just a holder for native code to access its SkBitmap. So this needs to + * get a version that is scaled to account for density. + */ + private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) { + Bitmap bitmap = bitmapDrawable.getBitmap(); + final int scaledWidth = bitmapDrawable.getIntrinsicWidth(); + final int scaledHeight = bitmapDrawable.getIntrinsicHeight(); + if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) { + return bitmap; + } + + Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF dst = new RectF(0, 0, scaledWidth, scaledHeight); + + Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig()); + Canvas canvas = new Canvas(scaled); + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(bitmap, src, dst, paint); + return scaled; + } + private void loadResource(Context context, Resources resources, @XmlRes int resourceId) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; @@ -452,7 +483,8 @@ public final class PointerIcon implements Parcelable { + "is different. All frames should have the exact same size and " + "share the same hotspot."); } - mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap(); + BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame; + mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame); } } } @@ -461,7 +493,8 @@ public final class PointerIcon implements Parcelable { + "refer to a bitmap drawable."); } - final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable); validateHotSpot(bitmap, hotSpotX, hotSpotY); // Set the properties now that we have successfully loaded the icon. mBitmap = bitmap; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2af246758812..3ff3c97620f0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7272,7 +7272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // becomes true where it should issue notifyViewEntered(). afm.notifyViewEntered(this); } - } else if (!isFocused()) { + } else if (!enter && !isFocused()) { afm.notifyViewExited(this); } } @@ -8010,6 +8010,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>Call {@link * android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)} * when the visibility of a virtual child changed. + * <li>Call + * {@link android.view.autofill.AutofillManager#notifyViewClicked(View, int)} when a virtual + * child is clicked. * <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure * changed and the current context should be committed (for example, when the user tapped * a {@code SUBMIT} button in an HTML page). @@ -8807,6 +8810,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Computes whether this virtual autofill view is visible to the user. * + * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy + * view must override it. + * * @return Whether the view is visible on the screen. */ public boolean isVisibleToUserForAutofill(int virtualId) { @@ -8819,7 +8825,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - return false; + return true; } /** diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 5ce2421aac87..cb1d89c54d9a 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -38,6 +38,7 @@ public final class AutofillId implements Parcelable { } /** @hide */ + @TestApi public AutofillId(AutofillId parent, int virtualChildId) { mVirtual = true; mViewId = parent.mViewId; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 41d05a5d1ed0..134dc1f060c6 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -71,10 +71,9 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +//TODO: use java.lang.ref.Cleaner once Android supports Java 9 import sun.misc.Cleaner; -// TODO: use java.lang.ref.Cleaner once Android supports Java 9 - /** * The {@link AutofillManager} provides ways for apps and custom views to integrate with the * Autofill Framework lifecycle. @@ -356,6 +355,13 @@ public final class AutofillManager { @GuardedBy("mLock") @Nullable private ArraySet<AutofillId> mFillableIds; + /** + * Views that were already "entered" - if they're entered again when the session is not active, + * they're ignored + * */ + @GuardedBy("mLock") + @Nullable private ArraySet<AutofillId> mEnteredIds; + /** If set, session is commited when the field is clicked. */ @GuardedBy("mLock") @Nullable private AutofillId mSaveTriggerId; @@ -711,17 +717,29 @@ public final class AutofillManager { } @GuardedBy("mLock") - private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) { + private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) { if (isDisabledByServiceLocked()) { if (sVerbose) { - Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view - + ") on state " + getStateAsStringLocked()); + Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id + + ") on state " + getStateAsStringLocked() + " because disabled by svc"); } return true; } - if (sVerbose && isFinishedLocked()) { - Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view - + ") on state " + getStateAsStringLocked()); + if (isFinishedLocked()) { + // Session already finished: ignore if automatic request and view already entered + if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null + && mEnteredIds.contains(id)) { + if (sVerbose) { + Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id + + ") on state " + getStateAsStringLocked() + + " because view was already entered: " + mEnteredIds); + } + return true; + } + } + if (sVerbose) { + Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id + + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds); } return false; } @@ -753,7 +771,8 @@ public final class AutofillManager { /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ @GuardedBy("mLock") private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) { - if (shouldIgnoreViewEnteredLocked(view, flags)) return null; + final AutofillId id = getAutofillId(view); + if (shouldIgnoreViewEnteredLocked(id, flags)) return null; AutofillCallback callback = null; @@ -766,7 +785,6 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { - final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); if (!isActiveLocked()) { @@ -776,6 +794,7 @@ public final class AutofillManager { // Update focus on existing session. updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); } + addEnteredIdLocked(id); } } return callback; @@ -900,8 +919,9 @@ public final class AutofillManager { @GuardedBy("mLock") private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds, int flags) { + final AutofillId id = getAutofillId(view, virtualId); AutofillCallback callback = null; - if (shouldIgnoreViewEnteredLocked(view, flags)) return callback; + if (shouldIgnoreViewEnteredLocked(id, flags)) return callback; ensureServiceClientAddedIfNeededLocked(); @@ -912,8 +932,6 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { - final AutofillId id = getAutofillId(view, virtualId); - if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, bounds, null, flags); @@ -921,11 +939,20 @@ public final class AutofillManager { // Update focus on existing session. updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags); } + addEnteredIdLocked(id); } } return callback; } + @GuardedBy("mLock") + private void addEnteredIdLocked(@NonNull AutofillId id) { + if (mEnteredIds == null) { + mEnteredIds = new ArraySet<>(1); + } + mEnteredIds.add(id); + } + /** * Called when a virtual view that supports autofill is exited. * @@ -992,9 +1019,9 @@ public final class AutofillManager { } if (!mEnabled || !isActiveLocked()) { - if (sVerbose && mEnabled) { - Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state " - + getStateAsStringLocked()); + if (sVerbose) { + Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + + "): ignoring on state " + getStateAsStringLocked()); } return; } @@ -1024,6 +1051,10 @@ public final class AutofillManager { } synchronized (mLock) { if (!mEnabled || !isActiveLocked()) { + if (sVerbose) { + Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId + + "): ignoring on state " + getStateAsStringLocked()); + } return; } @@ -1032,18 +1063,35 @@ public final class AutofillManager { } } + /** + * Called to indicate a {@link View} is clicked. + * + * @param view view that has been clicked. + */ + public void notifyViewClicked(@NonNull View view) { + notifyViewClicked(view.getAutofillId()); + } /** - * Called when a {@link View} is clicked. Currently only used by views that should trigger save. + * Called to indicate a virtual view has been clicked. * - * @hide + * @param view the virtual view parent. + * @param virtualId id identifying the virtual child inside the parent view. */ - public void notifyViewClicked(View view) { - final AutofillId id = view.getAutofillId(); + public void notifyViewClicked(@NonNull View view, int virtualId) { + notifyViewClicked(getAutofillId(view, virtualId)); + } + private void notifyViewClicked(AutofillId id) { + if (!hasAutofillFeature()) { + return; + } if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId); synchronized (mLock) { + if (!mEnabled || !isActiveLocked()) { + return; + } if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) { if (sDebug) Log.d(TAG, "triggering commit by click of " + id); commitLocked(); @@ -1058,16 +1106,16 @@ public final class AutofillManager { * * @hide */ - public void onActivityFinished() { + public void onActivityFinishing() { if (!hasAutofillFeature()) { return; } synchronized (mLock) { if (mSaveOnFinish) { - if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service"); + if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()"); commitLocked(); } else { - if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service"); + if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()"); cancelLocked(); } } @@ -1088,6 +1136,7 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } + if (sVerbose) Log.v(TAG, "commit() called by app"); synchronized (mLock) { commitLocked(); } @@ -1392,7 +1441,8 @@ public final class AutofillManager { if (sVerbose) { Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value + ", flags=" + flags + ", state=" + getStateAsStringLocked() - + ", compatMode=" + isCompatibilityModeEnabledLocked()); + + ", compatMode=" + isCompatibilityModeEnabledLocked() + + ", enteredIds=" + mEnteredIds); } if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) { if (sVerbose) { @@ -1430,7 +1480,7 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } @GuardedBy("mLock") @@ -1445,22 +1495,25 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } @GuardedBy("mLock") - private void resetSessionLocked() { + private void resetSessionLocked(boolean resetEnteredIds) { mSessionId = NO_SESSION; mState = STATE_UNKNOWN; mTrackedViews = null; mFillableIds = null; mSaveTriggerId = null; + if (resetEnteredIds) { + mEnteredIds = null; + } } @GuardedBy("mLock") private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, int flags) { - if (sVerbose && action != ACTION_VIEW_EXITED) { + if (sVerbose) { Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value + ", action=" + action + ", flags=" + flags); } @@ -1630,7 +1683,7 @@ public final class AutofillManager { mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0; if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) { // Reset the session state - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) { // Reset connection to system @@ -1826,7 +1879,7 @@ public final class AutofillManager { private void setSessionFinished(int newState) { synchronized (mLock) { if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState); - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ false); mState = newState; } } @@ -1954,6 +2007,7 @@ public final class AutofillManager { pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds); } pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds); + pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds); pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId); pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish); pw.print(pfx); pw.print("compat mode enabled: "); pw.println( @@ -2296,6 +2350,7 @@ public final class AutofillManager { final boolean[] isVisible; if (client.autofillClientIsVisibleForAutofill()) { + if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); isVisible = client.autofillClientGetViewVisibility(trackedIds); } else { // All false @@ -2315,7 +2370,7 @@ public final class AutofillManager { } if (sVerbose) { - Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " + Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): " + " mVisibleTrackedIds=" + mVisibleTrackedIds + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); } @@ -2421,6 +2476,9 @@ public final class AutofillManager { } if (mVisibleTrackedIds == null) { + if (sVerbose) { + Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); + } finishSessionLocked(); } } diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java index e80fdd93542c..1da998d01ba3 100644 --- a/core/java/android/view/autofill/AutofillPopupWindow.java +++ b/core/java/android/view/autofill/AutofillPopupWindow.java @@ -46,6 +46,7 @@ public class AutofillPopupWindow extends PopupWindow { private final WindowPresenter mWindowPresenter; private WindowManager.LayoutParams mWindowLayoutParams; + private boolean mFullScreen; private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @@ -104,12 +105,17 @@ public class AutofillPopupWindow extends PopupWindow { */ public void update(View anchor, int offsetX, int offsetY, int width, int height, Rect virtualBounds) { + mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT; // If we are showing the popup for a virtual view we use a fake view which // delegates to the anchor but present itself with the same bounds as the // virtual view. This ensures that the location logic in popup works // symmetrically when the dropdown is below and above the anchor. final View actualAnchor; - if (virtualBounds != null) { + if (mFullScreen) { + offsetX = 0; + offsetY = 0; + actualAnchor = anchor; + } else if (virtualBounds != null) { final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top}; actualAnchor = new View(anchor.getContext()) { @Override @@ -209,6 +215,17 @@ public class AutofillPopupWindow extends PopupWindow { } @Override + protected boolean findDropDownPosition(View anchor, LayoutParams outParams, + int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) { + if (mFullScreen) { + // Do not patch LayoutParams if force full screen + return false; + } + return super.findDropDownPosition(anchor, outParams, xOffset, yOffset, + width, height, gravity, allowScroll); + } + + @Override public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { if (sVerbose) { Log.v(TAG, "showAsDropDown(): anchor=" + anchor + ", xoff=" + xoff + ", yoff=" + yoff diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 2789bae15923..e4b2930a23cb 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -1,9 +1,11 @@ per-file TextView.java = siyamed@google.com per-file TextView.java = nona@google.com per-file TextView.java = clarabayarri@google.com + per-file EditText.java = siyamed@google.com per-file EditText.java = nona@google.com per-file EditText.java = clarabayarri@google.com + per-file Editor.java = siyamed@google.com per-file Editor.java = nona@google.com per-file Editor.java = clarabayarri@google.com diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index e91db1390582..7217def3cf08 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1583,7 +1583,7 @@ public class PopupWindow { * * @hide */ - protected final boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams, + protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams, int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) { final int anchorHeight = anchor.getHeight(); final int anchorWidth = anchor.getWidth(); diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java index 500c028ed4c6..bf151c39271a 100644 --- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java +++ b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java @@ -57,6 +57,8 @@ public class GradientDrawable extends Drawable { private int mMainColor; private int mSecondaryColor; private ValueAnimator mColorAnimation; + private int mMainColorTo; + private int mSecondaryColorTo; public GradientDrawable(@NonNull Context context) { mDensity = context.getResources().getDisplayMetrics().density; @@ -76,7 +78,7 @@ public class GradientDrawable extends Drawable { } public void setColors(int mainColor, int secondaryColor, boolean animated) { - if (mainColor == mMainColor && secondaryColor == mSecondaryColor) { + if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) { return; } @@ -84,6 +86,9 @@ public class GradientDrawable extends Drawable { mColorAnimation.cancel(); } + mMainColorTo = mainColor; + mSecondaryColorTo = mainColor; + if (animated) { final int mainFrom = mMainColor; final int secFrom = mSecondaryColor; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 6a56f452f3fe..4c5991ef8afb 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -5286,6 +5286,18 @@ public class BatteryStatsImpl extends BatteryStats { case TelephonyManager.NETWORK_TYPE_HSPAP: bin = DATA_CONNECTION_HSPAP; break; + case TelephonyManager.NETWORK_TYPE_GSM: + bin = DATA_CONNECTION_GSM; + break; + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + bin = DATA_CONNECTION_TD_SCDMA; + break; + case TelephonyManager.NETWORK_TYPE_IWLAN: + bin = DATA_CONNECTION_IWLAN; + break; + case TelephonyManager.NETWORK_TYPE_LTE_CA: + bin = DATA_CONNECTION_LTE_CA; + break; default: bin = DATA_CONNECTION_OTHER; break; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9c8997657474..30d81a7216a3 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -576,7 +576,8 @@ public class ZygoteInit { installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName, instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter, uuid, classLoaderContext, seInfo, false /* downgrade */, - targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null); + targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null, + "server-dexopt"); } catch (RemoteException | ServiceSpecificException e) { // Ignore (but log), we need this on the classpath for fallback mode. Log.w(TAG, "Failed compiling classpath element for system server: " diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java index 1916c11e9c9d..f44a1d122f39 100644 --- a/core/java/com/android/internal/print/DumpUtils.java +++ b/core/java/com/android/internal/print/DumpUtils.java @@ -213,10 +213,9 @@ public class DumpUtils { PrintAttributes.MediaSize mediaSize = attributes.getMediaSize(); if (mediaSize != null) { writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize); + proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait()); } - proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait()); - PrintAttributes.Resolution res = attributes.getResolution(); if (res != null) { writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res); diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index d6f8dc45560c..61d5031e16ed 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -604,6 +604,7 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, CameraInfo cameraInfo; status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo); if (rc != NO_ERROR) { + ALOGE("%s: getCameraInfo error: %d", __FUNCTION__, rc); return rc; } int defaultOrientation = 0; diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index c0950bfc0b20..9a53b89ffe30 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -46,18 +46,12 @@ import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/libs/incident/proto/android/os/header.proto"; +import "frameworks/base/libs/incident/proto/android/os/metadata.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; import "frameworks/base/libs/incident/proto/android/section.proto"; package android.os; -// This field contains internal metadata associated with an incident report, -// such as the section ids and privacy policy specs from caller as well as how long -// and how many bytes a section takes, etc. -message IncidentMetadata { - -} - // privacy field options must not be set at this level because all // the sections are able to be controlled and configured by section ids. // Instead privacy field options need to be configured in each section proto message. diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index 02fc4da4d220..6b92aa8e8ffa 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -422,6 +422,8 @@ message GlobalSettingsProto { optional SettingProto enable_deletion_helper_no_threshold_toggle = 340 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto notification_snooze_options = 341 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto enable_gnss_raw_meas_full_tracking = 346 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto install_carrier_app_notification_persistent = 356 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto install_carrier_app_notification_sleep_millis = 357 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto zram_enabled = 347 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto smart_replies_in_notifications_flags = 348 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/res/res/drawable/ic_signal_cellular_alt_24px.xml b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml new file mode 100644 index 000000000000..29f1f431c199 --- /dev/null +++ b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="20dp" + android:height="21dp" + android:viewportWidth="20" + android:viewportHeight="21"> + + <group + android:translateX="-31.000000" + android:translateY="-77.000000"> + <group + android:translateX="24.000000" + android:translateY="72.000000"> + <path + android:fillType="evenOdd" + android:strokeWidth="1" + android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z" /> + <path + android:fillColor="#4285F4" + android:strokeWidth="1" + android:pathData="M23,5 L27,5 L27,26 L23,26 L23,5 Z M7,18.125 L11,18.125 L11,26 L7,26 L7,18.125 Z +M15,11.5625 L19,11.5625 L19,26 L15,26 L15,11.5625 Z" /> + </group> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml new file mode 100644 index 000000000000..07298c182269 --- /dev/null +++ b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/autofill_dataset_picker" + style="@style/AutofillDatasetPicker" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/autofill_window_title" + android:layout_above="@+id/autofill_dataset_container" + android:layout_alignStart="@+id/autofill_dataset_container" + android:textSize="16sp"/> + + <!-- autofill_container is the common parent for inserting authentication item or + autofill_dataset_list--> + <FrameLayout + android:id="@+id/autofill_dataset_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true"> + <ListView + android:id="@+id/autofill_dataset_list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + android:divider="@null" + android:drawSelectorOnTop="true" + android:visibility="gone"/> + </FrameLayout> + +</RelativeLayout> diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml index 9a3d7361118c..8774334f11be 100644 --- a/core/res/res/values-mcc302-mnc220/config.xml +++ b/core/res/res/values-mcc302-mnc220/config.xml @@ -36,7 +36,7 @@ <!-- Values for GPS configuration (Telus) --> <string-array translatable="false" name="config_gpsParameters"> - <item>SUPL_HOST=supl.telusmobility.com</item> + <item>SUPL_HOST=supl.google.com</item> <item>SUPL_PORT=7275</item> <item>SUPL_VER=0x20000</item> <item>SUPL_MODE=1</item> diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml index 007fd045ced7..05896b18cfb8 100644 --- a/core/res/res/values-mcc302-mnc221/config.xml +++ b/core/res/res/values-mcc302-mnc221/config.xml @@ -34,7 +34,7 @@ <!-- Values for GPS configuration (Telus) --> <string-array translatable="false" name="config_gpsParameters"> - <item>SUPL_HOST=supl.telusmobility.com</item> + <item>SUPL_HOST=supl.google.com</item> <item>SUPL_PORT=7275</item> <item>SUPL_VER=0x20000</item> <item>SUPL_MODE=1</item> diff --git a/core/res/res/values-television/dimens.xml b/core/res/res/values-television/dimens.xml index 4c25225dd50c..aa5625124f29 100644 --- a/core/res/res/values-television/dimens.xml +++ b/core/res/res/values-television/dimens.xml @@ -20,4 +20,8 @@ <item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item> <item type="dimen" format="float" name="spot_shadow_alpha">0.3</item> + <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height --> + <dimen name="autofill_dataset_picker_max_width">60%</dimen> + <dimen name="autofill_dataset_picker_max_height">70%</dimen> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1f4425fef271..091129858736 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -999,7 +999,7 @@ <integer name="config_shortPressOnSleepBehavior">0</integer> <!-- Time to wait while a button is pressed before triggering a very long press. --> - <integer name="config_veryLongPressTimeout">6000</integer> + <integer name="config_veryLongPressTimeout">3500</integer> <!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] --> <string name="widget_default_package_name" translatable="false"></string> @@ -2328,6 +2328,10 @@ <string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false" >com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string> + <!-- Name of the dialog that is used to install the carrier app when the SIM is inserted --> + <string name="config_carrierAppInstallDialogComponent" translatable="false" + >com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string> + <!-- Apps that are authorized to access shared accounts, overridden by product overlays --> <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 0411c6ed8833..cfaab6ac2eec 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -626,7 +626,8 @@ <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen> <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height --> - <dimen name="autofill_dataset_picker_max_size">90%</dimen> + <dimen name="autofill_dataset_picker_max_width">90%</dimen> + <dimen name="autofill_dataset_picker_max_height">90%</dimen> <!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height --> <dimen name="autofill_save_custom_subtitle_max_height">20%</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 59c742e5d499..837113d4845e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -182,6 +182,8 @@ <string name="notification_channel_voice_mail">Voicemail messages</string> <!-- Telephony notification channel name for a channel containing wifi calling status notifications. --> <string name="notification_channel_wfc">Wi-Fi calling</string> + <!-- Telephony notification channel name for a channel containing SIM notifications --> + <string name="notification_channel_sim">SIM status</string> <!-- Displayed to tell the user that peer changed TTY mode --> <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string> @@ -2169,6 +2171,9 @@ <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] --> <string name="setup_autofill">Set up Autofill</string> + <!-- Title of fullscreen autofill window [CHAR-LIMIT=80] --> + <string name="autofill_window_title">Autofill</string> + <!-- String used to separate FirstName and LastName when writing out a local name e.g. John<separator>Smith [CHAR-LIMIT=NONE]--> <string name="autofill_address_name_separator">\u0020</string> @@ -3194,10 +3199,12 @@ <!-- See SIM_ADDED_DIALOG. This is the button of that dialog. --> <string name="sim_restart_button">Restart</string> <!-- See Carrier_App_Dialog. This is the message of that dialog. --> - <string name="carrier_app_dialog_message">To get your new SIM working properly, you\'ll need to install and open an app from your carrier.</string> - <!-- See Carrier_App_Dialog. This is the button of that dialog. --> - <string name="carrier_app_dialog_button">GET THE APP</string> - <string name="carrier_app_dialog_not_now">NOT NOW</string> + <string name="install_carrier_app_notification_title">Activate mobile service</string> + <string name="install_carrier_app_notification_text"> + Download the carrier app to activate your new SIM + </string> + <!-- See Carrier_App_Notification. This is the button of that dialog. --> + <string name="install_carrier_app_notification_button">Download app</string> <!-- See carrier_app_notification. This is the headline. --> <string name="carrier_app_notification_title">New SIM inserted</string> <string name="carrier_app_notification_text">Tap to set it up</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b69ded80009c..9995642ba455 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -574,6 +574,7 @@ <java-symbol type="string" name="notification_channel_sms" /> <java-symbol type="string" name="notification_channel_voice_mail" /> <java-symbol type="string" name="notification_channel_wfc" /> + <java-symbol type="string" name="notification_channel_sim" /> <java-symbol type="string" name="SetupCallDefault" /> <java-symbol type="string" name="accept" /> <java-symbol type="string" name="activity_chooser_view_see_all" /> @@ -1383,6 +1384,7 @@ <java-symbol type="drawable" name="ic_sim_card_multi_24px_clr" /> <java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" /> + <java-symbol type="drawable" name="ic_signal_cellular_alt_24px" /> <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" /> <java-symbol type="drawable" name="autofilled_highlight"/> @@ -2072,6 +2074,7 @@ <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" /> <java-symbol type="string" name="config_customVpnConfirmDialogComponent" /> <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" /> + <java-symbol type="string" name="config_carrierAppInstallDialogComponent" /> <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> <java-symbol type="string" name="config_persistentDataPackageName" /> @@ -2771,9 +2774,9 @@ <java-symbol type="array" name="resolver_target_actions_pin" /> <java-symbol type="array" name="resolver_target_actions_unpin" /> - <java-symbol type="string" name="carrier_app_dialog_message" /> - <java-symbol type="string" name="carrier_app_dialog_button" /> - <java-symbol type="string" name="carrier_app_dialog_not_now" /> + <java-symbol type="string" name="install_carrier_app_notification_title" /> + <java-symbol type="string" name="install_carrier_app_notification_text" /> + <java-symbol type="string" name="install_carrier_app_notification_button" /> <java-symbol type="string" name="carrier_app_notification_title" /> <java-symbol type="string" name="carrier_app_notification_text" /> <java-symbol type="string" name="negative_duration" /> @@ -2987,6 +2990,8 @@ <!-- com.android.server.autofill --> <java-symbol type="layout" name="autofill_save"/> <java-symbol type="layout" name="autofill_dataset_picker"/> + <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/> + <java-symbol type="id" name="autofill_dataset_container"/> <java-symbol type="id" name="autofill_dataset_list"/> <java-symbol type="id" name="autofill_dataset_picker"/> <java-symbol type="id" name="autofill" /> @@ -3015,7 +3020,8 @@ <java-symbol type="drawable" name="autofill_dataset_picker_background" /> <java-symbol type="style" name="AutofillDatasetPicker" /> <java-symbol type="style" name="AutofillSaveAnimation" /> - <java-symbol type="dimen" name="autofill_dataset_picker_max_size"/> + <java-symbol type="dimen" name="autofill_dataset_picker_max_width"/> + <java-symbol type="dimen" name="autofill_dataset_picker_max_height"/> <java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/> <java-symbol type="dimen" name="autofill_save_icon_max_size"/> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 9c3d8ebfa505..f6a11bd0352a 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -451,8 +451,8 @@ please see themes_device_defaults.xml. <item name="tooltipBackgroundColor">@color/tooltip_background_light</item> <!-- Autofill: max width/height of the dataset picker as a fraction of screen size --> - <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_size</item> - <item name="autofillDatasetPickerMaxHeight">@dimen/autofill_dataset_picker_max_size</item> + <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_width</item> + <item name="autofillDatasetPickerMaxHeight">@dimen/autofill_dataset_picker_max_height</item> <!-- Autofill: max height of custom save subtitle as a fraction of screen size --> <item name="autofillSaveCustomSubtitleMaxHeight">@dimen/autofill_save_custom_subtitle_max_height</item> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index b367f29da1a0..67c975496fbf 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -402,6 +402,8 @@ public class SettingsBackupTest { Settings.Global.GPU_DEBUG_APP, Settings.Global.GPU_DEBUG_LAYERS, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, + Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, + Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS, Settings.Global.NETWORK_ACCESS_TIMEOUT_MS, Settings.Global.WARNING_TEMPERATURE, Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY, diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS index 9f2182eca908..a35c6042bf53 100644 --- a/core/tests/coretests/src/android/text/OWNERS +++ b/core/tests/coretests/src/android/text/OWNERS @@ -1,3 +1,5 @@ +set noparent + siyamed@google.com nona@google.com -clarabayarri@google.com +clarabayarri@google.com
\ No newline at end of file diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index ee7abc5bd254..3cca47b47a59 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -25,7 +25,7 @@ import android.annotation.Nullable; import android.annotation.RawRes; import android.content.ContentResolver; import android.content.res.AssetFileDescriptor; -import android.content.res.AssetManager; +import android.content.res.AssetManager.AssetInputStream; import android.content.res.Resources; import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.Drawable; @@ -263,6 +263,63 @@ public final class ImageDecoder implements AutoCloseable { } } + /** + * Takes ownership of the AssetInputStream. + * + * @hide + */ + public static class AssetInputStreamSource extends Source { + public AssetInputStreamSource(@NonNull AssetInputStream ais, + @NonNull Resources res, @NonNull TypedValue value) { + mAssetInputStream = ais; + mResources = res; + + if (value.density == TypedValue.DENSITY_DEFAULT) { + mDensity = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + mDensity = value.density; + } else { + mDensity = Bitmap.DENSITY_NONE; + } + } + + private AssetInputStream mAssetInputStream; + private final Resources mResources; + private final int mDensity; + + @Override + public Resources getResources() { return mResources; } + + @Override + public int getDensity() { + return mDensity; + } + + @Override + public ImageDecoder createImageDecoder() throws IOException { + ImageDecoder decoder = null; + synchronized (this) { + if (mAssetInputStream == null) { + throw new IOException("Cannot reuse AssetInputStreamSource"); + } + AssetInputStream ais = mAssetInputStream; + mAssetInputStream = null; + try { + long asset = ais.getNativeAsset(); + decoder = nCreate(asset); + } finally { + if (decoder == null) { + IoUtils.closeQuietly(ais); + } else { + decoder.mInputStream = ais; + decoder.mOwnsInputStream = true; + } + } + return decoder; + } + } + } + private static class ResourceSource extends Source { ResourceSource(@NonNull Resources res, int resId) { mResources = res; @@ -296,11 +353,7 @@ public final class ImageDecoder implements AutoCloseable { mResDensity = value.density; } - if (!(is instanceof AssetManager.AssetInputStream)) { - // This should never happen. - throw new RuntimeException("Resource is not an asset?"); - } - long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); + long asset = ((AssetInputStream) is).getNativeAsset(); decoder = nCreate(asset); } finally { if (decoder == null) { @@ -444,6 +497,7 @@ public final class ImageDecoder implements AutoCloseable { private boolean mPreferRamOverQuality = false; private boolean mAsAlphaMask = false; private Rect mCropRect; + private Rect mOutPaddingRect; private Source mSource; private PostProcessor mPostProcessor; @@ -782,6 +836,18 @@ public final class ImageDecoder implements AutoCloseable { } /** + * Set a Rect for retrieving nine patch padding. + * + * If the image is a nine patch, this Rect will be set to the padding + * rectangle during decode. Otherwise it will not be modified. + * + * @hide + */ + public void setOutPaddingRect(@NonNull Rect outPadding) { + mOutPaddingRect = outPadding; + } + + /** * Specify whether the {@link Bitmap} should be mutable. * * <p>By default, a {@link Bitmap} created will be immutable, but that can @@ -892,7 +958,6 @@ public final class ImageDecoder implements AutoCloseable { postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, mPreferRamOverQuality, mAsAlphaMask); - } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -965,7 +1030,10 @@ public final class ImageDecoder implements AutoCloseable { if (np != null && NinePatch.isNinePatchChunk(np)) { Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); - Rect padding = new Rect(); + Rect padding = decoder.mOutPaddingRect; + if (padding == null) { + padding = new Rect(); + } nGetPadding(decoder.mNativePtr, padding); return new NinePatchDrawable(res, bm, np, padding, opticalInsets, null); @@ -1008,6 +1076,15 @@ public final class ImageDecoder implements AutoCloseable { final int srcDensity = computeDensity(src, decoder); Bitmap bm = decoder.decodeBitmap(); bm.setDensity(srcDensity); + + Rect padding = decoder.mOutPaddingRect; + if (padding != null) { + byte[] np = bm.getNinePatchChunk(); + if (np != null && NinePatch.isNinePatchChunk(np)) { + nGetPadding(decoder.mNativePtr, padding); + } + } + return bm; } } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 7ad062a6f6f9..44b783bb6e63 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Outline; @@ -49,6 +50,7 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @SuppressWarnings("unused") @Deprecated public BitmapDrawable(Resources res) { - mBitmapState = new BitmapState((Bitmap) null); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState((Bitmap) null), res); } /** @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null); + init(new BitmapState(bitmap), null); } /** @@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable { * the display metrics of the resources. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState(bitmap), res); } /** @@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); - } + this(null, filepath); } /** @@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + Bitmap bitmap = null; + try (FileInputStream stream = new FileInputStream(filepath)) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), + (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeFile() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } } } @@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); - } + this(null, is); } /** @@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + Bitmap bitmap = null; + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), + (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeStream() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } } } @@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable { } } + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + Bitmap bitmap = null; try (InputStream is = r.openRawResource(srcResId, value)) { - bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null); + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); } catch (Exception e) { // Do nothing and pick up the error below. } @@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable { } } + private BitmapDrawable(BitmapState state, Resources res) { + init(state, res); + } + /** - * The one constructor to rule them all. This is called by all public + * The one helper to rule them all. This is called by all public & private * constructors to set the state and initialize local properties. */ - private BitmapDrawable(BitmapState state, Resources res) { + private void init(BitmapState state, Resources res) { mBitmapState = state; - updateLocalState(res); + + if (mBitmapState != null && res != null) { + mBitmapState.mTargetDensity = mTargetDensity; + } } /** diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 05533d787aa1..8af2fd8bbb5e 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -50,11 +51,13 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.View; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1179,6 +1182,10 @@ public abstract class Drawable { return null; } + if (opts == null) { + return getBitmapDrawable(res, value, is); + } + /* ugh. The decodeStream contract is that we have already allocated the pad rect, but if the bitmap does not had a ninepatch chunk, then the pad will be ignored. If we could change this to lazily @@ -1194,7 +1201,6 @@ public abstract class Drawable { // an application in compatibility mode, without scaling those down // to the compatibility density only to have them scaled back up when // drawn to the screen. - if (opts == null) opts = new BitmapFactory.Options(); opts.inScreenDensity = Drawable.resolveDensity(res, 0); Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts); if (bm != null) { @@ -1211,6 +1217,33 @@ public abstract class Drawable { return null; } + private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) { + try { + ImageDecoder.Source source = null; + if (value != null) { + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + source = ImageDecoder.createSource(res, is, density); + } else { + source = ImageDecoder.createSource(res, is); + } + + return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (IOException e) { + /* do nothing. + If the exception happened on decode, the drawable will be null. + */ + Log.e("Drawable", "Unable to decode stream: " + e); + } + return null; + } + /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see @@ -1310,11 +1343,10 @@ public abstract class Drawable { } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); - try { - Bitmap bm = BitmapFactory.decodeFile(pathName); - if (bm != null) { - return drawableFromBitmap(null, bm, null, null, null, pathName); - } + try (FileInputStream stream = new FileInputStream(pathName)) { + return getBitmapDrawable(null, null, stream); + } catch(IOException e) { + // Do nothing; we will just return null if the FileInputStream had an error } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 17900204fa22..66f2a3173eae 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -24,9 +24,9 @@ import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -211,7 +211,8 @@ public class NinePatchDrawable extends Drawable { restoreAlpha = -1; } - final boolean needsDensityScaling = canvas.getDensity() == 0; + final boolean needsDensityScaling = canvas.getDensity() == 0 + && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity(); if (needsDensityScaling) { restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save(); @@ -421,10 +422,6 @@ public class NinePatchDrawable extends Drawable { final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0); if (srcResId != 0) { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = !state.mDither; - options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi; - final Rect padding = new Rect(); final Rect opticalInsets = new Rect(); Bitmap bitmap = null; @@ -433,7 +430,17 @@ public class NinePatchDrawable extends Drawable { final TypedValue value = new TypedValue(); final InputStream is = r.openRawResource(srcResId, value); - bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> { + decoder.setOutPaddingRect(padding); + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); is.close(); } catch (IOException e) { @@ -660,8 +667,9 @@ public class NinePatchDrawable extends Drawable { return; } - final int sourceDensity = ninePatch.getDensity(); final int targetDensity = mTargetDensity; + final int sourceDensity = ninePatch.getDensity() == Bitmap.DENSITY_NONE ? + targetDensity : ninePatch.getDensity(); final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets; if (sourceOpticalInsets != Insets.NONE) { diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index ded427eb244a..e2aba0401036 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -545,7 +545,9 @@ public class KeyStore { try { args = args != null ? args : new KeymasterArguments(); entropy = entropy != null ? entropy : new byte[0]; - return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid); + OperationResult res = mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid); + // This result is -26 (KEY_USER_NOT_AUTHENTICATED) but why?? + return res; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return null; @@ -563,7 +565,8 @@ public class KeyStore { try { arguments = arguments != null ? arguments : new KeymasterArguments(); input = input != null ? input : new byte[0]; - return mBinder.update(token, arguments, input); + OperationResult res = mBinder.update(token, arguments, input); + return res; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return null; @@ -618,9 +621,9 @@ public class KeyStore { * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode. */ - public int addAuthToken(byte[] authToken) { + public int addAuthToken(byte[] authToken, int userId) { try { - return mBinder.addAuthToken(authToken); + return mBinder.addAuthToken(authToken, userId); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; @@ -832,14 +835,14 @@ public class KeyStore { public InvalidKeyException getInvalidKeyException( String keystoreKeyAlias, int uid, KeyStoreException e) { switch (e.getErrorCode()) { - case LOCKED: + case LOCKED: // 2 return new UserNotAuthenticatedException(); - case KeymasterDefs.KM_ERROR_KEY_EXPIRED: + case KeymasterDefs.KM_ERROR_KEY_EXPIRED: // -25 return new KeyExpiredException(); - case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: + case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: // -2 return new KeyNotYetValidException(); - case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: - case OP_AUTH_NEEDED: + case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: // -26 + case OP_AUTH_NEEDED: // 15 { // We now need to determine whether the key/operation can become usable if user // authentication is performed, or whether it can never become usable again. @@ -879,7 +882,7 @@ public class KeyStore { // None of the key's SIDs can ever be authenticated return new KeyPermanentlyInvalidatedException(); } - case UNINITIALIZED: + case UNINITIALIZED: // 3 return new KeyPermanentlyInvalidatedException(); default: return new InvalidKeyException("Keystore operation failed", e); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index 09b3b9b523b4..419eb24e1cc1 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -243,13 +243,7 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), - spec.isUserAuthenticationRequired(), - spec.getUserAuthenticationValidityDurationSeconds(), - spec.isUserAuthenticationValidWhileOnBody(), - spec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - spec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec); } catch (IllegalStateException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } @@ -285,16 +279,7 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings); args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - KeymasterUtils.addUserAuthArgs(args, - spec.isUserAuthenticationRequired(), - spec.getUserAuthenticationValidityDurationSeconds(), - spec.isUserAuthenticationValidWhileOnBody(), - spec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - spec.isUserConfirmationRequired()); - if (spec.isTrustedUserPresenceRequired()) { - args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); - } + KeymasterUtils.addUserAuthArgs(args, spec); KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( args, mKeymasterAlgorithm, diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index e33e3cd4e92b..d68a33de2c61 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -344,13 +344,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), - mSpec.isUserAuthenticationRequired(), - mSpec.getUserAuthenticationValidityDurationSeconds(), - mSpec.isUserAuthenticationValidWhileOnBody(), - mSpec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - mSpec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec); } catch (IllegalArgumentException | IllegalStateException e) { throw new InvalidAlgorithmParameterException(e); } @@ -541,13 +535,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings); args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - KeymasterUtils.addUserAuthArgs(args, - mSpec.isUserAuthenticationRequired(), - mSpec.getUserAuthenticationValidityDurationSeconds(), - mSpec.isUserAuthenticationValidWhileOnBody(), - mSpec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - mSpec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(args, mSpec); args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart()); args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, mSpec.getKeyValidityForOriginationEnd()); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index 05cc74a0bec9..fc86ca0443b0 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -497,13 +497,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); - KeymasterUtils.addUserAuthArgs(importArgs, - spec.isUserAuthenticationRequired(), - spec.getUserAuthenticationValidityDurationSeconds(), - spec.isUserAuthenticationValidWhileOnBody(), - spec.isInvalidatedByBiometricEnrollment(), - spec.getBoundToSpecificSecureUserId(), - spec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(importArgs, spec); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, @@ -700,13 +694,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( params.getEncryptionPaddings()); args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); - KeymasterUtils.addUserAuthArgs(args, - params.isUserAuthenticationRequired(), - params.getUserAuthenticationValidityDurationSeconds(), - params.isUserAuthenticationValidWhileOnBody(), - params.isInvalidatedByBiometricEnrollment(), - params.getBoundToSpecificSecureUserId(), - params.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(args, params); KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( args, keymasterAlgorithm, diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index da23c70f58bb..d0814c6f2f93 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.KeyguardManager; import android.hardware.fingerprint.FingerprintManager; +import android.security.GateKeeper; import android.security.KeyStore; import android.text.TextUtils; @@ -232,7 +233,7 @@ import javax.security.auth.x500.X500Principal; * key = (SecretKey) keyStore.getKey("key2", null); * }</pre> */ -public final class KeyGenParameterSpec implements AlgorithmParameterSpec { +public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs { private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); @@ -265,6 +266,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private final boolean mInvalidatedByBiometricEnrollment; private final boolean mIsStrongBoxBacked; private final boolean mUserConfirmationRequired; + private final boolean mUnlockedDeviceRequired; /** * @hide should be built with Builder @@ -295,7 +297,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, boolean isStrongBoxBacked, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -344,6 +347,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mIsStrongBoxBacked = isStrongBoxBacked; mUserConfirmationRequired = userConfirmationRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -669,6 +673,22 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#SetUnlockedDeviceRequired(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** + * @hide + */ + public long getBoundToSpecificSecureUserId() { + return GateKeeper.INVALID_SECURE_USER_ID; + } + + /** * Builder of {@link KeyGenParameterSpec} instances. */ public final static class Builder { @@ -699,6 +719,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private boolean mInvalidatedByBiometricEnrollment = true; private boolean mIsStrongBoxBacked = false; private boolean mUserConfirmationRequired; + private boolean mUnlockedDeviceRequired = false; /** * Creates a new instance of the {@code Builder}. @@ -1267,6 +1288,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Sets whether the keystore requires the screen to be unlocked before allowing decryption + * using this key. If this is set to {@code true}, any attempt to decrypt using this key + * while the screen is locked will fail. A locked device requires a PIN, password, + * fingerprint, or other trusted factor to access. + */ + @NonNull + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + mUnlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1297,7 +1330,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mIsStrongBoxBacked, - mUserConfirmationRequired); + mUserConfirmationRequired, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index b5b328192f21..7f8259b89962 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -212,7 +212,7 @@ import javax.crypto.Mac; * ... * }</pre> */ -public final class KeyProtection implements ProtectionParameter { +public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; @@ -229,6 +229,8 @@ public final class KeyProtection implements ProtectionParameter { private final long mBoundToSecureUserId; private final boolean mCriticalToDeviceEncryption; private final boolean mUserConfirmationRequired; + private final boolean mTrustedUserPresenceRequired; + private final boolean mUnlockedDeviceRequired; private KeyProtection( Date keyValidityStart, @@ -242,11 +244,13 @@ public final class KeyProtection implements ProtectionParameter { boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, + boolean trustedUserPresenceRequired, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, long boundToSecureUserId, boolean criticalToDeviceEncryption, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -265,6 +269,8 @@ public final class KeyProtection implements ProtectionParameter { mBoundToSecureUserId = boundToSecureUserId; mCriticalToDeviceEncryption = criticalToDeviceEncryption; mUserConfirmationRequired = userConfirmationRequired; + mTrustedUserPresenceRequired = trustedUserPresenceRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -437,6 +443,14 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Returns {@code true} if the key is authorized to be used only if a test of user presence has + * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls. + */ + public boolean isTrustedUserPresenceRequired() { + return mTrustedUserPresenceRequired; + } + + /** * Returns {@code true} if the key will be de-authorized when the device is removed from the * user's body. This option has no effect on keys that don't have an authentication validity * duration, and has no effect if the device lacks an on-body sensor. @@ -494,6 +508,15 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#SetRequireDeviceUnlocked(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** * Builder of {@link KeyProtection} instances. */ public final static class Builder { @@ -512,6 +535,9 @@ public final class KeyProtection implements ProtectionParameter { private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; private boolean mUserConfirmationRequired; + private boolean mTrustedUserPresenceRequired = false; + private boolean mUnlockedDeviceRequired = false; + private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; @@ -811,6 +837,16 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Sets whether a test of user presence is required to be performed between the + * {@code Signature.initSign()} and {@code Signature.sign()} method calls. + */ + @NonNull + public Builder setTrustedUserPresenceRequired(boolean required) { + mTrustedUserPresenceRequired = required; + return this; + } + + /** * Sets whether the key will remain authorized only until the device is removed from the * user's body up to the limit of the authentication validity period (see * {@link #setUserAuthenticationValidityDurationSeconds} and @@ -892,6 +928,18 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Sets whether the keystore requires the screen to be unlocked before allowing decryption + * using this key. If this is set to {@code true}, any attempt to decrypt using this key + * while the screen is locked will fail. A locked device requires a PIN, password, + * fingerprint, or other trusted factor to access. + */ + @NonNull + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + mUnlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + /** * Builds an instance of {@link KeyProtection}. * * @throws IllegalArgumentException if a required field is missing @@ -910,11 +958,13 @@ public final class KeyProtection implements ProtectionParameter { mRandomizedEncryptionRequired, mUserAuthenticationRequired, mUserAuthenticationValidityDurationSeconds, + mTrustedUserPresenceRequired, mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mBoundToSecureUserId, mCriticalToDeviceEncryption, - mUserConfirmationRequired); + mUserConfirmationRequired, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 4e28601f17a1..5bd0e7406ff9 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -18,6 +18,7 @@ package android.security.keystore; import android.util.Log; import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; import android.security.GateKeeper; import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; @@ -101,22 +102,27 @@ public abstract class KeymasterUtils { * require user authentication. */ public static void addUserAuthArgs(KeymasterArguments args, - boolean userAuthenticationRequired, - int userAuthenticationValidityDurationSeconds, - boolean userAuthenticationValidWhileOnBody, - boolean invalidatedByBiometricEnrollment, - long boundToSpecificSecureUserId, - boolean userConfirmationRequired) { - if (userConfirmationRequired) { + UserAuthArgs spec) { + if (spec.isTrustedUserPresenceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); + } + + if (spec.isUserConfirmationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); } - if (!userAuthenticationRequired) { + if (spec.isUnlockedDeviceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED); + // Once keymaster is properly ignoring this tag, it should be added to every auth list + args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, UserHandle.getCallingUserId()); + } + + if (!spec.isUserAuthenticationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); return; } - if (userAuthenticationValidityDurationSeconds == -1) { + if (spec.getUserAuthenticationValidityDurationSeconds() == -1) { // Every use of this key needs to be authorized by the user. This currently means // fingerprint-only auth. FingerprintManager fingerprintManager = @@ -132,9 +138,9 @@ public abstract class KeymasterUtils { } long sid; - if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) { - sid = boundToSpecificSecureUserId; - } else if (invalidatedByBiometricEnrollment) { + if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) { + sid = spec.getBoundToSpecificSecureUserId(); + } else if (spec.isInvalidatedByBiometricEnrollment()) { // The fingerprint-only SID will change on fingerprint enrollment or removal of all, // enrolled fingerprints, invalidating the key. sid = fingerprintOnlySid; @@ -147,14 +153,14 @@ public abstract class KeymasterUtils { args.addUnsignedLong( KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid)); args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT); - if (userAuthenticationValidWhileOnBody) { + if (spec.isUserAuthenticationValidWhileOnBody()) { throw new ProviderException("Key validity extension while device is on-body is not " + "supported for keys requiring fingerprint authentication"); } } else { long sid; - if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) { - sid = boundToSpecificSecureUserId; + if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) { + sid = spec.getBoundToSpecificSecureUserId(); } else { // The key is authorized for use for the specified amount of time after the user has // authenticated. Whatever unlocks the secure lock screen should authorize this key. @@ -165,8 +171,8 @@ public abstract class KeymasterUtils { args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT); args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, - userAuthenticationValidityDurationSeconds); - if (userAuthenticationValidWhileOnBody) { + spec.getUserAuthenticationValidityDurationSeconds()); + if (spec.isUserAuthenticationValidWhileOnBody()) { args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); } } diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java new file mode 100644 index 000000000000..3a7017ecaa88 --- /dev/null +++ b/keystore/java/android/security/keystore/UserAuthArgs.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore; + +/** + * @hide + * + * This is an interface to encapsulate the user authentication arguments that + * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent + * authorization characteristics for new or imported keys can implement this + * interface to be passed to that method. + */ +public interface UserAuthArgs { + + boolean isUserAuthenticationRequired(); + int getUserAuthenticationValidityDurationSeconds(); + boolean isUserAuthenticationValidWhileOnBody(); + boolean isInvalidatedByBiometricEnrollment(); + boolean isTrustedUserPresenceRequired(); + boolean isUnlockedDeviceRequired(); + boolean isUserConfirmationRequired(); + long getBoundToSpecificSecureUserId(); + +} diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk index b63400f14609..08c834699f40 100644 --- a/libs/incident/Android.mk +++ b/libs/incident/Android.mk @@ -33,6 +33,7 @@ LOCAL_SRC_FILES := \ ../../core/java/android/os/IIncidentManager.aidl \ ../../core/java/android/os/IIncidentReportStatusListener.aidl \ proto/android/os/header.proto \ + proto/android/os/metadata.proto \ src/IncidentReportArgs.cpp LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include diff --git a/libs/incident/proto/android/os/metadata.proto b/libs/incident/proto/android/os/metadata.proto new file mode 100644 index 000000000000..a1e89b0dbf98 --- /dev/null +++ b/libs/incident/proto/android/os/metadata.proto @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +option java_multiple_files = true; + +package android.os; + +// This field contains internal metadata associated with an incident report, +// such as the section ids and privacy policy specs from caller as well as how long +// and how many bytes a section takes, etc. +message IncidentMetadata { + + // privacy level of the incident report. + enum Destination { + AUTOMATIC = 0; + EXPLICIT = 1; + LOCAL = 2; + } + optional Destination dest = 1; + + optional int32 request_size = 2; + + optional bool use_dropbox = 3; + + // stats of each section taken in this incident report. + message SectionStats { + // section id. + optional int32 id = 1; + // if the section is successfully taken. + optional bool success = 2; + // number of bytes in the report after filtering. + optional int32 report_size_bytes = 3; + // the total duration to execute the section. + optional int64 exec_duration_ms = 4; + + // number of bytes dumped from the section directly. + optional int32 dump_size_bytes = 5; + // duration of the dump takes. + optional int64 dump_duration_ms = 6; + // true if the section timed out. + optional bool timed_out = 7; + // true if the section is truncated. + optional bool is_truncated = 8; + + // Next Tag: 9 + } + repeated SectionStats sections = 4; +} + diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 823410f6bb76..9ad5cd93e1e2 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityThread; import android.hardware.Camera; @@ -278,6 +279,7 @@ public class MediaRecorder implements AudioRouting * third-party applications. * </p> */ + @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int REMOTE_SUBMIX = 8; /** Microphone audio source tuned for unprocessed (raw) sound if available, behaves like @@ -303,6 +305,7 @@ public class MediaRecorder implements AudioRouting * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; } diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 219063564132..343bbdaf27d5 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -400,6 +400,7 @@ public class AudioPolicy { new AudioAttributes.Builder() .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX) .addTag(addressForTag(mix)) + .addTag(AudioRecord.SUBMIX_FIXED_VOLUME) .build(), mixFormat, AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(), diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS new file mode 100644 index 000000000000..7057ce6cb6fe --- /dev/null +++ b/packages/CarrierDefaultApp/OWNERS @@ -0,0 +1,12 @@ +tgunn@google.com +breadley@google.com +hallliu@google.com +rgreenwalt@google.com +mpq@google.com +amitmahajan@google.com +fionaxu@google.com +jackyu@google.com +jminjie@google.com +satk@google.com +shuoq@google.com +refuhoo@google.com
\ No newline at end of file diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 44f68eca49a3..d73a5d73e5bf 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -3117,6 +3117,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private final Consumer<String> mCallback; + private boolean mIsTransformationStarted; + public DocumentTransformer(Context context, PrintJobInfo printJob, MutexFileProvider fileProvider, PrintAttributes attributes, Consumer<String> callback) { @@ -3144,29 +3146,35 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat @Override public void onServiceConnected(ComponentName name, IBinder service) { - final IPdfEditor editor = IPdfEditor.Stub.asInterface(service); - new AsyncTask<Void, Void, String>() { - @Override - protected String doInBackground(Void... params) { - // It's OK to access the data members as they are - // final and this code is the last one to touch - // them as shredding is the very last step, so the - // UI is not interactive at this point. - try { - doTransform(editor); - updatePrintJob(); - return null; - } catch (IOException | RemoteException | IllegalStateException e) { - return e.toString(); + // We might get several onServiceConnected if the service crashes and restarts. + // mIsTransformationStarted makes sure that we only try once. + if (!mIsTransformationStarted) { + final IPdfEditor editor = IPdfEditor.Stub.asInterface(service); + new AsyncTask<Void, Void, String>() { + @Override + protected String doInBackground(Void... params) { + // It's OK to access the data members as they are + // final and this code is the last one to touch + // them as shredding is the very last step, so the + // UI is not interactive at this point. + try { + doTransform(editor); + updatePrintJob(); + return null; + } catch (IOException | RemoteException | IllegalStateException e) { + return e.toString(); + } } - } - @Override - protected void onPostExecute(String error) { - mContext.unbindService(DocumentTransformer.this); - mCallback.accept(error); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + @Override + protected void onPostExecute(String error) { + mContext.unbindService(DocumentTransformer.this); + mCallback.accept(error); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + mIsTransformationStarted = true; + } } @Override diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java index 7c2e55f35cde..cf73aacb9b55 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java @@ -285,6 +285,11 @@ public final class SelectPrinterActivity extends Activity implements final int position = ((AdapterContextMenuInfo) menuInfo).position; PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position); + // Printer is null if this is a context menu for the "add printer" entry + if (printer == null) { + return; + } + menu.setHeaderTitle(printer.getName()); // Add the select menu item if applicable. diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 769b7e9a13c5..1e8c523a9e34 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -870,6 +870,12 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, GlobalSettingsProto.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING); + dumpSetting(s, p, + Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, + GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT); + dumpSetting(s, p, + Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS, + GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS); // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated. dumpSetting(s, p, Settings.Global.LOW_POWER_MODE, diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk new file mode 100644 index 000000000000..00a2e60b29f4 --- /dev/null +++ b/packages/SimAppDialog/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := SimAppDialog +LOCAL_CERTIFICATE := platform + + +LOCAL_STATIC_ANDROID_LIBRARIES := \ + android-support-v4 + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +include frameworks/opt/setupwizard/library/common-platform-deprecated.mk + +include $(BUILD_PACKAGE) diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml new file mode 100644 index 000000000000..873f6c5bac54 --- /dev/null +++ b/packages/SimAppDialog/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.simappdialog"> + <application android:label="@string/app_name"> + <activity + android:name=".InstallCarrierAppActivity" + android:exported="true" + android:permission="android.permission.NETWORK_SETTINGS" + android:theme="@style/SuwThemeGlif.Light"> + </activity> + </application> +</manifest> diff --git a/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml new file mode 100644 index 000000000000..85896e8a2687 --- /dev/null +++ b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="21dp" + android:height="22dp" + android:viewportWidth="21" + android:viewportHeight="22"> + + <group + android:translateX="-196.000000" + android:translateY="-77.000000"> + <group + android:translateX="190.000000" + android:translateY="72.000000"> + <path + android:fillType="evenOdd" + android:strokeWidth="1" + android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z"/> + <group + android:translateX="6.666667" + android:translateY="5.333333"> + <path + android:fillColor="#4285F4" + android:strokeWidth="1" + android:pathData="M 17 0 L 19 0 Q 20 0 20 1 L 20 20.3333333 Q 20 21.3333333 19 21.3333333 L 17 21.3333333 Q 16 21.3333333 16 20.3333333 L 16 1 Q 16 0 17 0 Z"/> + <path + android:fillColor="#4285F4" + android:strokeWidth="1" + android:pathData="M 1 13.3333333 L 3 13.3333333 Q 4 13.3333333 4 14.3333333 L 4 20.3333333 Q 4 21.3333333 3 21.3333333 L 1 21.3333333 Q 0 21.3333333 0 20.3333333 L 0 14.3333333 Q 0 13.3333333 1 13.3333333 Z"/> + <path + android:fillColor="#4285F4" + android:strokeWidth="1" + android:pathData="M 9 6.66666667 L 11 6.66666667 Q 12 6.66666667 12 7.66666667 L 12 20.33333337 Q 12 21.33333337 11 21.33333337 L 9 21.33333337 Q 8 21.33333337 8 20.33333337 L 8 7.66666667 Q 8 6.66666667 9 6.66666667 Z"/> + </group> + </group> + </group> +</vector>
\ No newline at end of file diff --git a/packages/SimAppDialog/res/drawable/placeholder.xml b/packages/SimAppDialog/res/drawable/placeholder.xml new file mode 100644 index 000000000000..53eee7437c16 --- /dev/null +++ b/packages/SimAppDialog/res/drawable/placeholder.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- TODO(b/72511181): replace when illustration is finished --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="270dp" + android:height="270dp" + android:viewportHeight="270.0" + android:viewportWidth="270.0"> + <path android:fillColor="#E8EAED" + android:pathData="M183.54,265H84.88c-7.63,0 -13.81,-6.18 -13.81,-13.81V18.81C71.07,11.18 77.25,5 84.88,5h98.66c7.63,0 13.81,6.18 13.81,13.81v232.38C197.35,258.82 191.17,265 183.54,265z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M183.54,6.63c6.72,0 12.18,5.46 12.18,12.18v232.38c0,6.72 -5.46,12.18 -12.18,12.18H84.88c-6.72,0 -12.18,-5.46 -12.18,-12.18V18.81c0,-6.72 5.46,-12.18 12.18,-12.18H183.54M183.54,5H84.88c-7.63,0 -13.81,6.18 -13.81,13.81v232.38c0,7.63 6.18,13.81 13.81,13.81h98.66c7.63,0 13.81,-6.18 13.81,-13.81V18.81C197.35,11.18 191.17,5 183.54,5L183.54,5z"/> + <path android:fillColor="#FFFFFF" + android:pathData="M186.34,243.74H82.08c-2.41,0 -4.36,-1.95 -4.36,-4.36V30.61c0,-2.41 1.95,-4.36 4.36,-4.36h104.26c2.41,0 4.36,1.95 4.36,4.36v208.78C190.7,241.79 188.75,243.74 186.34,243.74z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M156.07,254.78h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18v-0.08c0,-0.65 0.53,-1.18 1.18,-1.18h43.72c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,254.25 156.72,254.78 156.07,254.78z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M156.07,17.67h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18V16.4c0,-0.65 0.53,-1.18 1.18,-1.18l43.72,0c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,17.14 156.72,17.67 156.07,17.67z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M197.85,84.16h-0.5V67.51h0.5c0.6,0 1.08,0.48 1.08,1.08v14.5C198.93,83.68 198.45,84.16 197.85,84.16z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M197.41,136.45h-0.06v-32.87h0.06c0.84,0 1.52,0.68 1.52,1.52v29.84C198.93,135.77 198.25,136.45 197.41,136.45z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M119.3,74.73l2.71,2.71c6.74,-6.74 17.67,-6.74 24.4,0l2.71,-2.71C140.89,66.49 127.54,66.49 119.3,74.73zM130.15,85.57l4.07,4.07l4.07,-4.07C136.04,83.33 132.39,83.33 130.15,85.57zM124.72,80.15l2.71,2.71c3.74,-3.74 9.82,-3.74 13.56,0l2.71,-2.71C138.46,74.91 129.96,74.91 124.72,80.15z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M143.7,179h-1.36v-2.71h-2.71V179h-10.85v-2.71h-2.71V179h-1.36c-1.5,0 -2.7,1.21 -2.7,2.71l-0.01,18.98c0,1.5 1.21,2.71 2.71,2.71h18.98c1.5,0 2.71,-1.21 2.71,-2.71v-18.98C146.41,180.22 145.2,179 143.7,179zM143.7,200.7h-18.98v-14.91h18.98V200.7zM127.43,188.49h6.78v6.78h-6.78V188.49z"/> + <path android:fillColor="#BDC1C6" + android:pathData="M146.41,144.49v-18.98c0,-1.5 -1.21,-2.71 -2.71,-2.71h-18.98c-1.5,0 -2.71,1.21 -2.71,2.71v18.98c0,1.5 1.21,2.71 2.71,2.71h18.98C145.2,147.2 146.41,145.99 146.41,144.49zM129.47,137.03l3.39,4.07l4.75,-6.11l6.1,8.13h-18.98L129.47,137.03z"/> +</vector> diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml new file mode 100644 index 000000000000..0462a9365c90 --- /dev/null +++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<com.android.setupwizardlib.GlifLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/setup_wizard_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:icon="@drawable/ic_signal_cellular_alt_rounded_24px" + app:suwHeaderText="@string/install_carrier_app_title" + app:suwFooter="@layout/install_carrier_app_footer"> + + <LinearLayout + style="@style/SuwContentFrame" + android:id="@+id/content_frame" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:id="@+id/install_carrier_app_description" + style="@style/SuwDescription.Glif" + android:text="@string/install_carrier_app_description_default" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <com.android.setupwizardlib.view.FillContentLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <!-- TODO(b/72511181): final illo and content description update --> + <ImageView + android:src="@drawable/placeholder" + style="@style/SuwContentIllustration" + android:contentDescription="@null" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + + </com.android.setupwizardlib.view.FillContentLayout> + </LinearLayout> + +</com.android.setupwizardlib.GlifLayout> diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml new file mode 100644 index 000000000000..10dcb77a6584 --- /dev/null +++ b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.setupwizardlib.view.ButtonBarLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/footer" + style="@style/SuwGlifButtonBar.Stackable" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <Button + android:id="@+id/skip_button" + style="@style/SuwGlifButton.Secondary" + android:text="@string/install_carrier_app_defer_action" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> + + <Button + android:id="@+id/download_button" + style="@style/SuwGlifButton.Primary" + android:text="@string/install_carrier_app_download_action" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</com.android.setupwizardlib.view.ButtonBarLayout> diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml new file mode 100644 index 000000000000..0c3930dfdf08 --- /dev/null +++ b/packages/SimAppDialog/res/values/strings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- TODO character limits --> + <!-- The name of this application --> + <string name="app_name">Sim App Dialog</string> + <!-- Install Carrier App Activity --> + <!-- Title of screen asking user to download the carrier app to match the inserted SIM card --> + <string name="install_carrier_app_title">Activate mobile service</string> + <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we know the name of the carrier--> + <string name="install_carrier_app_description">To get your new SIM working properly, you\'ll + need to install the <xliff:g name="carrier_name" example="Project Fi">%1$s</xliff:g> app + </string> + <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we don't know the name of the carrier--> + <string name="install_carrier_app_description_default">To get your new SIM working properly, + you\'ll need to install the carrier app + </string> + <!-- Name of the button used to defer downloading the carrier app --> + <string name="install_carrier_app_defer_action">Not now</string> + <!-- Name of the button for downloading the carrier app --> + <string name="install_carrier_app_download_action">Download app</string> +</resources>
\ No newline at end of file diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java new file mode 100644 index 000000000000..9e9b80d39ed7 --- /dev/null +++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.simappdialog; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.android.setupwizardlib.util.WizardManagerHelper; + +/** + * Activity that gives a user the choice to download the SIM app or defer until a later time + * + * Will finish with result {@link #DEFER_RESULT} on defer button press or {@link #DOWNLOAD_RESULT} + * if the download button is pressed + * + * Can display the carrier app name if its passed into the intent with key + * {@link #BUNDLE_KEY_CARRIER_NAME} + */ +public class InstallCarrierAppActivity extends Activity implements View.OnClickListener { + /** + * Key for the carrier app name that will be displayed as the app to download. If unset, a + * default description will be used + */ + public static final String BUNDLE_KEY_CARRIER_NAME = "carrier_name"; + /** Result code when the defer button is pressed */ + public static final int DEFER_RESULT = 1; + /** Result code when the download button is pressed */ + public static final int DOWNLOAD_RESULT = 2; + + @Override + protected void onCreate(Bundle icicle) { + // Setup theme for aosp/pixel + setTheme( + WizardManagerHelper.getThemeRes( + SystemProperties.get("setupwizard.theme"), + R.style.SuwThemeGlif_Light + ) + ); + + super.onCreate(icicle); + setContentView(R.layout.install_carrier_app_activity); + + Button notNowButton = findViewById(R.id.skip_button); + notNowButton.setOnClickListener(this); + + Button downloadButton = findViewById(R.id.download_button); + downloadButton.setOnClickListener(this); + + // Include carrier name in description text if its present in the intent + Intent intent = getIntent(); + if (intent != null) { + String carrierName = intent.getStringExtra(BUNDLE_KEY_CARRIER_NAME); + if (!TextUtils.isEmpty(carrierName)) { + TextView subtitle = findViewById(R.id.install_carrier_app_description); + subtitle.setText(getString(R.string.install_carrier_app_description, carrierName)); + } + } + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.skip_button: + finish(DEFER_RESULT); + break; + case R.id.download_button: + finish(DOWNLOAD_RESULT); + break; + } + } + + private void finish(int resultCode) { + setResult(resultCode); + finish(); + } +} diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step.png Binary files differnew file mode 100644 index 000000000000..d7f94497409a --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..7c657036c4fc --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..eea819a1109e --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..504ceb79fba0 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..8e7d8cba1a4e --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..456a68f0200c --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..fb854ec51296 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..75d184a1f739 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..9e0af28efced --- /dev/null +++ b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..7c00bd5d1f30 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-xhdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..81b44665e078 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..724aa9e19e41 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-xxhdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..7ba0d1b1b7d3 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..a175ccb5b4f8 --- /dev/null +++ b/packages/SystemUI/res/drawable-land-xxxhdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..45ce1d4b62b6 --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..6da0c9e6bd5e --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..71e89595f35d --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..bb7ae2637fcc --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..32b9ded52836 --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..ed1949c71438 --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step.png Binary files differnew file mode 100644 index 000000000000..d888869d2b0f --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..dc3b25c0d347 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..d4e5a94fca1b --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..0e693f7d6e9a --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..07577999201f --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..4f07ec1a9797 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step.png Binary files differnew file mode 100644 index 000000000000..ba5b457a0bfa --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..a55ea1d5b1ab --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..407ef28a3f2a --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..39cfbf2afb48 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..a7fd3a69e98c --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..f2a1255c3570 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step.png Binary files differnew file mode 100644 index 000000000000..5a7eec6502af --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..f7abb54f974f --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step.png Binary files differnew file mode 100644 index 000000000000..a1f44dc598b2 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..175a9aefa463 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step.png Binary files differnew file mode 100644 index 000000000000..0fb93ca2a18d --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step_dark.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step_dark.png Binary files differnew file mode 100644 index 000000000000..1052940af382 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_quick_step_dark.png diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/SystemUI/res/drawable/car_ic_hvac.xml new file mode 100644 index 000000000000..bdc44b38a176 --- /dev/null +++ b/packages/SystemUI/res/drawable/car_ic_hvac.xml @@ -0,0 +1,51 @@ +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="37dp" + android:height="31dp" + android:viewportWidth="37" + android:viewportHeight="31"> + + <group + android:translateX="-4.000000" + android:translateY="-6.000000"> + <group + android:translateX="5.000000" + android:translateY="5.000000"> + <path + android:fillType="evenOdd" + android:strokeColor="#FAFAFA" + android:strokeWidth="3.5" + android:pathData="M0.320769938,6.07518051 C6.46754647,1.46509811 12.4222362,1.46509811 +18.1848392,6.07518051 C23.9474422,10.6852629 29.3258717,10.4931761 +34.3201276,5.49892021" /> + <path + android:fillType="evenOdd" + android:strokeColor="#FAFAFA" + android:strokeWidth="3.5" + android:pathData="M0.320769938,17.0751805 C6.46754647,12.4650981 12.4222362,12.4650981 +18.1848392,17.0751805 C23.9474422,21.6852629 29.3258717,21.4931761 +34.3201276,16.4989202" /> + <path + android:fillType="evenOdd" + android:strokeColor="#FAFAFA" + android:strokeWidth="3.5" + android:pathData="M0.320769938,28.0751805 C6.46754647,23.4650981 12.4222362,23.4650981 +18.1848392,28.0751805 C23.9474422,32.6852629 29.3258717,32.4931761 +34.3201276,27.4989202" /> + </group> + </group> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml new file mode 100644 index 000000000000..61d937b90d04 --- /dev/null +++ b/packages/SystemUI/res/drawable/car_ic_notification.xml @@ -0,0 +1,28 @@ +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="56dp" + android:height="56dp" + android:viewportWidth="48" + android:viewportHeight="48"> + + <path + android:fillColor="#FFFFFF" + android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4 +4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73 +1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8 +9v12z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml new file mode 100644 index 000000000000..4651dcb3a229 --- /dev/null +++ b/packages/SystemUI/res/drawable/car_ic_overview.xml @@ -0,0 +1,28 @@ +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="56dp" + android:height="56dp" + android:viewportWidth="48" + android:viewportHeight="48"> + + <path + android:pathData="M0 0h48v48H0z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82 +0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" /> +</vector> diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml new file mode 100644 index 000000000000..f432d366e926 --- /dev/null +++ b/packages/SystemUI/res/layout/car_facet_button.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2017, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/car_facet_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="center" + android:animateLayoutChanges="true" + android:orientation="vertical"> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/car_nav_button_icon" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:animateLayoutChanges="true" + android:background="@android:color/transparent" + android:scaleType="fitCenter"> + </com.android.keyguard.AlphaOptimizedImageButton> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/car_nav_button_more_icon" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:animateLayoutChanges="true" + android:background="@android:color/transparent" + android:scaleType="fitCenter"> + </com.android.keyguard.AlphaOptimizedImageButton> + + </LinearLayout> +</merge> diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/SystemUI/res/layout/car_left_navigation_bar.xml new file mode 100644 index 000000000000..866b5a5b0ec7 --- /dev/null +++ b/packages/SystemUI/res/layout/car_left_navigation_bar.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:background="@drawable/system_bar_background"> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:id="@+id/nav_buttons" + android:orientation="vertical" + android:gravity="top" + android:paddingTop="30dp" + android:layout_weight="1" + android:background="@drawable/system_bar_background" + android:animateLayoutChanges="true"> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/home" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end" + android:src="@drawable/car_ic_overview" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvac" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end" + systemui:broadcast="true" + android:src="@drawable/car_ic_hvac" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="bottom" + android:orientation="vertical"> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/notifications" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:src="@drawable/car_ic_notification" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="20dp" + android:paddingBottom="20dp" + android:alpha="0.7" + /> + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_clock_end_padding" + android:gravity="center_horizontal" + android:paddingBottom="20dp" + /> + + <Space + android:layout_height="10dp" + android:layout_width="match_parent"/> + + </LinearLayout> + +</com.android.systemui.statusbar.car.CarNavigationBarView> diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml index 999dbac3b8bc..4666c604dc9c 100644 --- a/packages/SystemUI/res/layout/car_navigation_bar.xml +++ b/packages/SystemUI/res/layout/car_navigation_bar.xml @@ -19,34 +19,80 @@ <com.android.systemui.statusbar.car.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent" - android:gravity="center" android:background="@drawable/system_bar_background"> - <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed - rotation so skip this level of the heirarchy. - --> <LinearLayout android:layout_height="match_parent" - android:layout_width="@dimen/car_navigation_bar_width" + android:layout_width="wrap_content" android:orientation="horizontal" - android:clipChildren="false" - android:clipToPadding="false" android:id="@+id/nav_buttons" + android:gravity="left" + android:paddingLeft="30dp" + android:layout_weight="1" android:animateLayoutChanges="true"> - <!-- Buttons get populated here from a car_arrays.xml. --> + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/home" + android:layout_height="match_parent" + android:layout_width="wrap_content" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end" + android:src="@drawable/car_ic_overview" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="30dp" + android:paddingRight="30dp" + /> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvac" + android:layout_height="match_parent" + android:layout_width="wrap_content" + systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end" + systemui:broadcast="true" + android:src="@drawable/car_ic_hvac" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="30dp" + android:paddingRight="30dp" + /> </LinearLayout> - <!-- lights out layout to match exactly --> <LinearLayout + android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_width="match_parent" - android:orientation="horizontal" - android:id="@+id/lights_out" - android:visibility="gone"> - <!-- Must match nav_buttons. --> + android:layout_weight="1" + android:gravity="right" + android:orientation="horizontal"> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/notifications" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:src="@drawable/car_ic_notification" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="20dp" + android:paddingRight="20dp" + android:alpha="0.7" + /> + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_clock_end_padding" + android:gravity="center_vertical" + android:paddingRight="20dp" + /> + + <Space + android:layout_width="10dp" + android:layout_height="match_parent"/> + </LinearLayout> </com.android.systemui.statusbar.car.CarNavigationBarView> + diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml index 767764694fae..4062eb8068fa 100644 --- a/packages/SystemUI/res/layout/car_navigation_button.xml +++ b/packages/SystemUI/res/layout/car_navigation_button.xml @@ -17,28 +17,13 @@ */ --> -<com.android.systemui.statusbar.car.CarNavigationButton - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:orientation="horizontal" - android:background="?android:attr/selectableItemBackground"> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/car_nav_button_icon" - android:layout_height="match_parent" - android:layout_width="@dimen/car_navigation_button_width" - android:layout_centerInParent="true" - android:animateLayoutChanges="true" - android:scaleType="fitCenter"> + android:id="@+id/car_nav_button_icon" + android:layout_height="wrap_content" + android:layout_width="@dimen/car_navigation_button_width" + android:layout_centerInParent="true" + android:animateLayoutChanges="true" + android:scaleType="fitCenter"> </com.android.keyguard.AlphaOptimizedImageButton> - - <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/car_nav_button_more_icon" - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:layout_centerVertical="true" - android:layout_toRightOf="@+id/car_nav_button_icon" - android:animateLayoutChanges="true" - android:scaleType="fitCenter"> - </com.android.keyguard.AlphaOptimizedImageButton> -</com.android.systemui.statusbar.car.CarNavigationButton> +</merge> diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml new file mode 100644 index 000000000000..99ab8021b43b --- /dev/null +++ b/packages/SystemUI/res/layout/car_right_navigation_bar.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:background="@drawable/system_bar_background"> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:id="@+id/nav_buttons" + android:orientation="vertical" + android:gravity="top" + android:paddingTop="30dp" + android:layout_weight="1" + android:background="@drawable/system_bar_background" + android:animateLayoutChanges="true"> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/home" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end" + android:src="@drawable/car_ic_overview" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvac" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end" + systemui:broadcast="true" + android:src="@drawable/car_ic_hvac" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="bottom" + android:orientation="vertical"> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/notifications" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:src="@drawable/car_ic_notification" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="20dp" + android:paddingBottom="20dp" + android:alpha="0.7" + /> + + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_clock_end_padding" + android:gravity="center_horizontal" + android:paddingBottom="20dp" + /> + + <Space + android:layout_height="10dp" + android:layout_width="match_parent"/> + + </LinearLayout> + +</com.android.systemui.statusbar.car.CarNavigationBarView> diff --git a/packages/SystemUI/res/layout/quick_settings_header.xml b/packages/SystemUI/res/layout/quick_settings_header.xml new file mode 100644 index 000000000000..43197c400139 --- /dev/null +++ b/packages/SystemUI/res/layout/quick_settings_header.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<com.android.systemui.qs.QSTooltipView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_header_tooltip_height" + android:alpha="0" + android:gravity="center_horizontal|bottom" + android:visibility="invisible"> + + <TextView + android:id="@+id/header_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/quick_settings_header_onboarding_text" + android:textAppearance="@style/TextAppearance.QS.TileLabel" + android:textColor="?android:attr/colorAccent" /> + +</com.android.systemui.qs.QSTooltipView> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index a5e37d529bee..13ca11401c03 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -40,7 +40,7 @@ <dimen name="battery_detail_graph_space_top">27dp</dimen> <dimen name="battery_detail_graph_space_bottom">27dp</dimen> - <dimen name="qs_tile_margin_top">16dp</dimen> + <dimen name="qs_tile_margin_top">32dp</dimen> <dimen name="qs_brightness_padding_top">6dp</dimen> <dimen name="qs_detail_margin_top">28dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml index 7541b0e8c084..9197bb51e1a6 100644 --- a/packages/SystemUI/res/values/arrays_tv.xml +++ b/packages/SystemUI/res/values/arrays_tv.xml @@ -30,7 +30,7 @@ <item>com.google.android.apps.mediashell/.settings.CastSettingsActivity</item> <item>com.google.android.katniss.setting/.SpeechSettingsActivity</item> <item>com.google.android.katniss.setting/.SearchSettingsActivity</item> - <item>com.google.android.gsf.notouch/.UsageDiagnosticsSettingActivity</item> + <item>com.google.android.tungsten.setupwraith/.settings.usage.UsageDiagnosticsSettingActivity</item> <item>com.google.android.tvlauncher/.notifications.NotificationsSidePanelActivity</item> </string-array> </resources> diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml new file mode 100644 index 000000000000..b1097c39363c --- /dev/null +++ b/packages/SystemUI/res/values/attrs_car.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- Allow for custom attribs to be added to a facet button --> + <declare-styleable name="CarFacetButton"> + <!-- icon to be rendered (drawable) --> + <attr name="icon" format="reference"/> + <!-- intent to start when button is click --> + <attr name="intent" format="string"/> + <!-- intent to start when a long press has happened --> + <attr name="longIntent" format="string"/> + <!-- categories that will be added as extras to the fired intents --> + <attr name="categories" format="string"/> + <!-- package names that will be added as extras to the fired intents --> + <attr name="packages" format="string" /> + </declare-styleable> + + + <!-- Allow for custom attribs to be added to a nav button --> + <declare-styleable name="CarNavigationButton"> + <!-- intent to start when button is click --> + <attr name="intent" format="string"/> + <!-- intent to start when a long press has happened --> + <attr name="longIntent" format="string"/> + <!-- start the intent as a broad cast instead of an activity if true--> + <attr name="broadcast" format="boolean"/> + </declare-styleable> +</resources> diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml index 9c8dcb1b975a..db829f25802e 100644 --- a/packages/SystemUI/res/values/config_car.xml +++ b/packages/SystemUI/res/values/config_car.xml @@ -22,4 +22,9 @@ uri that will be launched into the docked window. --> <bool name="config_enablePersistentDockedActivity">false</bool> <string name="config_persistentDockedActivityIntentUri" translatable="false"></string> + + <!-- configure which system ui bars should be displayed --> + <bool name="config_enableLeftNavigationBar">false</bool> + <bool name="config_enableRightNavigationBar">false</bool> + <bool name="config_enableBottomNavigationBar">true</bool> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d11ab4298b82..bc828ff8efb4 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -290,7 +290,7 @@ <dimen name="qs_tile_height">106dp</dimen> <dimen name="qs_tile_margin">19dp</dimen> - <dimen name="qs_tile_margin_top">16dp</dimen> + <dimen name="qs_tile_margin_top">32dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> <dimen name="qs_header_gear_translation">16dp</dimen> @@ -309,6 +309,7 @@ <dimen name="qs_tile_padding_bottom">16dp</dimen> <dimen name="qs_tile_spacing">4dp</dimen> <dimen name="qs_panel_padding_bottom">0dp</dimen> + <dimen name="qs_panel_padding_top">32dp</dimen> <dimen name="qs_detail_header_height">56dp</dimen> <dimen name="qs_detail_header_padding">0dp</dimen> <dimen name="qs_detail_image_width">56dp</dimen> @@ -333,6 +334,9 @@ <dimen name="qs_detail_item_icon_width">32dp</dimen> <dimen name="qs_detail_item_icon_marginStart">0dp</dimen> <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen> + <dimen name="qs_header_padding_start">16dp</dimen> + <dimen name="qs_header_padding_end">24dp</dimen> + <dimen name="qs_header_tooltip_height">32dp</dimen> <dimen name="qs_footer_padding_start">16dp</dimen> <dimen name="qs_footer_padding_end">24dp</dimen> <dimen name="qs_footer_icon_size">16dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8c59e75315a1..86cab22a2756 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -666,6 +666,8 @@ <!-- Textual description of Ethernet connections --> <string name="ethernet_label">Ethernet</string> + <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] --> + <string name="quick_settings_header_onboarding_text">Press & hold on the icons for more options</string> <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] --> <string name="quick_settings_dnd_label">Do not disturb</string> <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index adb4e33d1a19..8b577400357d 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -47,6 +47,7 @@ public final class Prefs { Key.QS_INVERT_COLORS_ADDED, Key.QS_WORK_ADDED, Key.QS_NIGHTDISPLAY_ADDED, + Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, Key.SEEN_MULTI_USER, Key.NUM_APPS_LAUNCHED, Key.HAS_SEEN_RECENTS_ONBOARDING, @@ -76,6 +77,11 @@ public final class Prefs { String QS_WORK_ADDED = "QsWorkAdded"; @Deprecated String QS_NIGHTDISPLAY_ADDED = "QsNightDisplayAdded"; + /** + * Used for tracking how many times the user has seen the long press tooltip in the Quick + * Settings panel. + */ + String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount"; String SEEN_MULTI_USER = "HasSeenMultiUser"; String NUM_APPS_LAUNCHED = "NumAppsLaunched"; String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding"; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 222c6e8274f5..fccd9ceb5c97 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -44,6 +44,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha public static final float EXPANDED_TILE_DELAY = .86f; + private final ArrayList<View> mAllViews = new ArrayList<>(); /** * List of {@link View}s representing Quick Settings that are being animated from the quick QS @@ -65,6 +66,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private TouchAnimator mNonfirstPageDelayedAnimator; private TouchAnimator mBrightnessAnimator; + /** + * Whether the animation is stable and not in the middle of animating between the collapsed and + * expanded states. + */ + private boolean mIsInStableState; private boolean mOnKeyguard; private boolean mAllowFancy; @@ -89,6 +95,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha Log.w(TAG, "QS Not using page layout"); } panel.setPageListener(this); + + // At time of creation, the QS panel is always considered stable as it's not in the middle + // of collapse/expanded. + mIsInStableState = true; } public void onRtlChanged() { @@ -243,6 +253,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } else { mBrightnessAnimator = null; } + View headerView = mQsPanel.getHeaderView(); + if (headerView!= null) { + firstPageBuilder.addFloat(headerView, "translationY", heightDiff, 0); + mAllViews.add(headerView); + } mFirstPageAnimator = firstPageBuilder .setListener(this) .build(); @@ -326,11 +341,21 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onAnimationAtStart() { + if (!mIsInStableState) { + mQsPanel.onCollapse(); + } + mIsInStableState = true; + mQuickQsPanel.setVisibility(View.VISIBLE); } @Override public void onAnimationAtEnd() { + if (!mIsInStableState) { + mQsPanel.onExpanded(); + } + mIsInStableState = true; + mQuickQsPanel.setVisibility(View.INVISIBLE); final int N = mQuickQsViews.size(); for (int i = 0; i < N; i++) { @@ -340,6 +365,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onAnimationStarted() { + if (mIsInStableState) { + mQsPanel.onAnimating(); + } + mIsInStableState = false; + mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE); if (mOnFirstPage) { final int N = mQuickQsViews.size(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index f7c388db0840..5640be55d2d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -18,6 +18,7 @@ package com.android.systemui.qs; import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; @@ -58,6 +59,7 @@ import java.util.Collection; public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener { public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness"; + public static final String QS_SHOW_LONG_PRESS_TOOLTIP = "qs_show_long_press"; protected final Context mContext; protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>(); @@ -72,6 +74,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private BrightnessController mBrightnessController; protected QSTileHost mHost; + protected QSTooltipView mTooltipView; protected QSSecurityFooter mFooter; private boolean mGridContentVisible = true; @@ -94,6 +97,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne setOrientation(VERTICAL); + mTooltipView = (QSTooltipView) LayoutInflater.from(mContext) + .inflate(R.layout.quick_settings_header, this, false); + mBrightnessView = LayoutInflater.from(mContext).inflate( R.layout.quick_settings_brightness_dialog, this, false); mTileLayout = new TileLayout(mContext); @@ -101,7 +107,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne Space space = new Space(mContext); space.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, mContext.getResources().getDimensionPixelSize(R.dimen.qs_footer_height))); - mScrollLayout = new QSScrollLayout(mContext, mBrightnessView, (View) mTileLayout, space); + mScrollLayout = new QSScrollLayout( + mContext, + mTooltipView, + mBrightnessView, + (View) mTileLayout, + space); addView(mScrollLayout); addDivider(); @@ -134,7 +145,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - Dependency.get(TunerService.class).addTunable(this, QS_SHOW_BRIGHTNESS); + final TunerService tunerService = Dependency.get(TunerService.class); + tunerService.addTunable(this, QS_SHOW_BRIGHTNESS); + tunerService.addTunable(this, QS_SHOW_LONG_PRESS_TOOLTIP); + if (mHost != null) { setTiles(mHost.getTiles()); } @@ -166,11 +180,16 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne @Override public void onTuningChanged(String key, String newValue) { if (QS_SHOW_BRIGHTNESS.equals(key)) { - mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0 - ? VISIBLE : GONE); + updateViewVisibilityForTuningValue(mBrightnessView, newValue); + } else if (QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) { + updateViewVisibilityForTuningValue(mTooltipView, newValue); } } + private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) { + view.setVisibility(newValue == null || Integer.parseInt(newValue) != 0 ? VISIBLE : GONE); + } + public void openDetails(String subPanel) { QSTile tile = getTile(subPanel); showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0}); @@ -205,6 +224,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne return mBrightnessView; } + View getHeaderView() { + return mTooltipView; + } + public void setCallback(QSDetail.Callback callback) { mCallback = callback; } @@ -266,11 +289,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mCustomizePanel != null && mCustomizePanel.isShown()) { mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2); } + + // Instantly hide the header here since we don't want it to still be animating. + mTooltipView.setVisibility(View.INVISIBLE); + } + + /** + * Called when the panel is fully animated out/expanded. This is different from the state + * tracked by {@link #mExpanded}, which only checks if the panel is even partially pulled out. + */ + public void onExpanded() { + mTooltipView.fadeIn(); + } + + public void onAnimating() { + mTooltipView.fadeOut(); } public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; mExpanded = expanded; + if (!mExpanded) { if (mTileLayout instanceof PagedTileLayout) { ((PagedTileLayout) mTileLayout).setCurrentItem(0, false); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java new file mode 100644 index 000000000000..d1f9741ba6c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.qs; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import com.android.systemui.Prefs; + +import java.util.concurrent.TimeUnit; + + +/** + * Tooltip/header view for the Quick Settings panel. + */ +public class QSTooltipView extends LinearLayout { + + private static final int FADE_ANIMATION_DURATION_MS = 300; + private static final long AUTO_FADE_OUT_DELAY_MS = TimeUnit.SECONDS.toMillis(6); + private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0; + public static final int MAX_TOOLTIP_SHOWN_COUNT = 3; + + private final Handler mHandler = new Handler(); + private final Runnable mAutoFadeOutRunnable = () -> fadeOut(); + + private int mShownCount; + + public QSTooltipView(Context context) { + this(context, null); + } + + public QSTooltipView(Context context, AttributeSet attrs) { + super(context, attrs); + mShownCount = getStoredShownCount(); + } + + /** Returns the latest stored tooltip shown count from SharedPreferences. */ + private int getStoredShownCount() { + return Prefs.getInt( + mContext, + Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, + TOOLTIP_NOT_YET_SHOWN_COUNT); + } + + /** + * Fades in the header view if we can show the tooltip - short circuits any running animation. + */ + public void fadeIn() { + if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) { + animate().cancel(); + setVisibility(View.VISIBLE); + animate() + .alpha(1f) + .setDuration(FADE_ANIMATION_DURATION_MS) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mHandler.postDelayed(mAutoFadeOutRunnable, AUTO_FADE_OUT_DELAY_MS); + } + }) + .start(); + + // Increment and drop the shown count in prefs for the next time we're deciding to + // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet + // in prefs (say, from a long press). + if (getStoredShownCount() <= mShownCount) { + Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount); + } + } + } + + /** + * Fades out the header view if it's partially visible - short circuits any running animation. + */ + public void fadeOut() { + animate().cancel(); + if (getVisibility() == View.VISIBLE && getAlpha() != 0f) { + mHandler.removeCallbacks(mAutoFadeOutRunnable); + animate() + .alpha(0f) + .setDuration(FADE_ANIMATION_DURATION_MS) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + perhapsMakeViewInvisible(); + } + }) + .start(); + } else { + perhapsMakeViewInvisible(); + } + } + + /** + * Only update visibility if the view is currently being shown. Otherwise, it's already been + * hidden by some other manner. + */ + private void perhapsMakeViewInvisible() { + if (getVisibility() == View.VISIBLE) { + setVisibility(View.INVISIBLE); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 83148558ea1d..1b4b7dfb310f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -124,9 +124,8 @@ public class QuickQSPanel extends QSPanel { @Override public void onTuningChanged(String key, String newValue) { - // No tunings for you. - if (key.equals(QS_SHOW_BRIGHTNESS)) { - // No Brightness for you. + if (QS_SHOW_BRIGHTNESS.equals(key) || QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) { + // No Brightness or Tooltip for you! super.onTuningChanged(key, "0"); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 65135ab142d7..9fa7bebf6ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -99,7 +99,11 @@ public class TileLayout extends ViewGroup implements QSTileLayout { record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight)); previousView = record.tileView.updateAccessibilityOrder(previousView); } - int height = (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin); + + // Only include the top margin in our measurement if we have more than 1 row to show. + // Otherwise, don't add the extra margin buffer at top. + int height = (mCellHeight + mCellMargin) * rows + + rows != 0 ? (mCellMarginTop - mCellMargin) : 0; if (height < 0) height = 0; setMeasuredDimension(width, height); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java index 37f2528205f7..6263efa2c711 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java @@ -107,7 +107,7 @@ public class TouchAnimator { void onAnimationAtStart(); /** - * Called when the animator moves into a position of "0". Start and end delays are + * Called when the animator moves into a position of "1". Start and end delays are * taken into account, so this position may cover a range of fractional inputs. */ void onAnimationAtEnd(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 7259282935a0..016cbd6f6675 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -42,6 +42,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.Utils; import com.android.systemui.Dependency; +import com.android.systemui.Prefs; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSIconView; @@ -49,6 +50,7 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.PagedTileLayout.TilePage; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.QSTooltipView; import java.util.ArrayList; @@ -191,6 +193,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { public void longClick() { mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION))); mHandler.sendEmptyMessage(H.LONG_CLICK); + + Prefs.putInt( + mContext, + Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, + QSTooltipView.MAX_TOOLTIP_SHOWN_COUNT); } public LogMaker populate(LogMaker logMaker) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 6205e9afcb03..2d31669db6c2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -240,6 +240,7 @@ public class DndTile extends QSTileImpl<BooleanState> { public void handleSetListening(boolean listening) { if (mListening == listening) return; mListening = listening; + if (mController == null) return; if (mListening) { mController.addCallback(mZenCallback); Prefs.registerListener(mContext, mPrefListener); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index b3ff4e5b890c..12daff1f12f9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -98,6 +98,8 @@ public class NfcTile extends QSTileImpl<BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled); final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled); + + if (getAdapter() == null) return; state.value = getAdapter().isEnabled(); state.label = mContext.getString(R.string.quick_settings_nfc_label); state.icon = new DrawableIcon(state.value ? mEnable : mDisable); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java new file mode 100644 index 000000000000..53101a5bd61f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java @@ -0,0 +1,161 @@ +package com.android.systemui.statusbar.car; + +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.android.keyguard.AlphaOptimizedImageButton; +import com.android.systemui.R; + +/** + * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined + * category. It can also render a indicator impling that there are more options of apps to launch + * using this component. This is done with a "More icon" currently an arrow as defined in the layout + * file. The class is to serve as an example. + * Usage example: A button that allows a user to select a music app and indicate that there are + * other music apps installed. + */ +public class CarFacetButton extends LinearLayout { + private static final float SELECTED_ALPHA = 1f; + private static final float UNSELECTED_ALPHA = 0.7f; + + private static final String FACET_FILTER_DELIMITER = ";"; + /** + * Extra information to be sent to a helper to make the decision of what app to launch when + * clicked. + */ + private static final String EXTRA_FACET_CATEGORIES = "categories"; + private static final String EXTRA_FACET_PACKAGES = "packages"; + private static final String EXTRA_FACET_ID = "filter_id"; + private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker"; + + private Context mContext; + private AlphaOptimizedImageButton mIcon; + private AlphaOptimizedImageButton mMoreIcon; + private boolean mSelected = false; + /** App categories that are to be used with this widget */ + private String[] mFacetCategories; + /** App packages that are allowed to be used with this widget */ + private String[] mFacetPackages; + + + public CarFacetButton(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + View.inflate(context, R.layout.car_facet_button, this); + + // extract custom attributes + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton); + setupIntents(typedArray); + setupIcons(typedArray); + } + + /** + * Reads the custom attributes to setup click handlers for this component. + */ + private void setupIntents(TypedArray typedArray) { + String intentString = typedArray.getString(R.styleable.CarFacetButton_intent); + String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent); + String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories); + String packageString = typedArray.getString(R.styleable.CarFacetButton_packages); + try { + final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME); + intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId())); + + if (packageString != null) { + mFacetPackages = packageString.split(FACET_FILTER_DELIMITER); + intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages); + } + if (categoryString != null) { + mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER); + intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories); + } + + setOnClickListener(v -> { + intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected); + mContext.startActivity(intent); + }); + + if (longPressIntentString != null) { + final Intent longPressIntent = Intent.parseUri(longPressIntentString, + Intent.URI_INTENT_SCHEME); + setOnLongClickListener(v -> { + mContext.startActivity(longPressIntent); + return true; + }); + } + } catch (Exception e) { + throw new RuntimeException("Failed to attach intent", e); + } + } + + + private void setupIcons(TypedArray styledAttributes) { + mIcon = findViewById(R.id.car_nav_button_icon); + mIcon.setScaleType(ImageView.ScaleType.CENTER); + mIcon.setClickable(false); + mIcon.setAlpha(UNSELECTED_ALPHA); + int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0); + if (iconResourceId == 0) { + throw new RuntimeException("specified icon resource was not found and is required"); + } + mIcon.setImageResource(iconResourceId); + + mMoreIcon = findViewById(R.id.car_nav_button_more_icon); + mMoreIcon.setClickable(false); + mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow)); + mMoreIcon.setAlpha(UNSELECTED_ALPHA); + mMoreIcon.setVisibility(GONE); + } + + /** + * @return The app categories the component represents + */ + public String[] getCategories() { + if (mFacetCategories == null) { + return new String[0]; + } + return mFacetCategories; + } + + /** + * @return The valid packages that should be considered. + */ + public String[] getFacetPackages() { + if (mFacetPackages == null) { + return new String[0]; + } + return mFacetPackages; + } + + /** + * Updates the alpha of the icons to "selected" and shows the "More icon" + * @param selected true if the view must be selected, false otherwise + */ + public void setSelected(boolean selected) { + super.setSelected(selected); + setSelected(selected, selected); + } + + /** + * Updates the visual state to let the user know if it's been selected. + * @param selected true if should update the alpha of the icon to selected, false otherwise + * @param showMoreIcon true if the "more icon" should be shown, false otherwise + */ + public void setSelected(boolean selected, boolean showMoreIcon) { + mSelected = selected; + if (selected) { + mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); + mMoreIcon.setAlpha(SELECTED_ALPHA); + mIcon.setAlpha(SELECTED_ALPHA); + } else { + mMoreIcon.setVisibility(GONE); + mIcon.setAlpha(UNSELECTED_ALPHA); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java new file mode 100644 index 000000000000..e8c9a5e5693a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java @@ -0,0 +1,114 @@ +package com.android.systemui.statusbar.car; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.view.View; +import android.view.ViewGroup; + +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +/** + * CarFacetButtons placed on the nav bar are designed to have visual indication that the active + * application on screen is associated with it. This is basically a similar concept to a radio + * button group. + */ +public class CarFacetButtonController { + + protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>(); + protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>(); + protected CarFacetButton mSelectedFacetButton; + protected Context mContext; + + public CarFacetButtonController(Context context) { + mContext = context; + } + + /** + * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons + * such that it can select and unselect them based on running task chages + * @param bar that may contain CarFacetButtons + */ + public void addCarNavigationBar(CarNavigationBarView bar) { + findFacets(bar); + } + + private void findFacets(ViewGroup root) { + final int childCount = root.getChildCount(); + + for (int i = 0; i < childCount; ++i) { + final View v = root.getChildAt(i); + if (v instanceof CarFacetButton) { + CarFacetButton facetButton = (CarFacetButton) v; + String[] categories = facetButton.getCategories(); + for (int j = 0; j < categories.length; j++) { + String category = categories[j]; + mButtonsByCategory.put(category, facetButton); + } + + String[] facetPackages = facetButton.getFacetPackages(); + for (int j = 0; j < facetPackages.length; j++) { + String facetPackage = facetPackages[j]; + mButtonsByPackage.put(facetPackage, facetButton); + } + } else if (v instanceof ViewGroup) { + findFacets((ViewGroup) v); + } + } + } + + + /** + * This will unselect the currently selected CarFacetButton and determine which one should be + * selected next. It does this by reading the properties on the CarFacetButton and seeing if + * they are a match with the supplied taskino. + * @param taskInfo of the currently running application + */ + public void taskChanged(ActivityManager.RunningTaskInfo taskInfo) { + if (taskInfo == null || taskInfo.baseActivity == null) { + return; + } + String packageName = taskInfo.baseActivity.getPackageName(); + + // If the package name belongs to a filter, then highlight appropriate button in + // the navigation bar. + if (mSelectedFacetButton != null) { + mSelectedFacetButton.setSelected(false); + } + CarFacetButton facetButton = mButtonsByPackage.get(packageName); + if (facetButton != null) { + facetButton.setSelected(true); + mSelectedFacetButton = facetButton; + } else { + String category = getPackageCategory(packageName); + if (category != null) { + facetButton = mButtonsByCategory.get(category); + facetButton.setSelected(true); + mSelectedFacetButton = facetButton; + } + } + } + + protected String getPackageCategory(String packageName) { + PackageManager pm = mContext.getPackageManager(); + Set<String> supportedCategories = mButtonsByCategory.keySet(); + for (String category : supportedCategories) { + Intent intent = new Intent(); + intent.setPackage(packageName); + intent.setAction(Intent.ACTION_MAIN); + intent.addCategory(category); + List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); + if (list.size() > 0) { + // Cache this package name into facetPackageMap, so we won't have to query + // all categories next time this package name shows up. + mButtonsByPackage.put(packageName, mButtonsByCategory.get(category)); + return category; + } + } + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java deleted file mode 100644 index 64c52ed6d29f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.car; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.support.v4.util.SimpleArrayMap; -import android.text.TextUtils; -import android.util.Log; -import android.util.SparseBooleanArray; -import android.view.View; -import android.widget.LinearLayout; -import com.android.systemui.R; - -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; - -/** - * A controller to populate data for CarNavigationBarView and handle user interactions. - * - * <p>Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can - * customize the navigation buttons by updating arrays_car.xml appropriately in an overlay. - */ -class CarNavigationBarController { - private static final String TAG = "CarNavBarController"; - - private static final String EXTRA_FACET_CATEGORIES = "categories"; - private static final String EXTRA_FACET_PACKAGES = "packages"; - private static final String EXTRA_FACET_ID = "filter_id"; - private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker"; - - /** - * Each facet of the navigation bar maps to a set of package names or categories defined in - * arrays_car.xml. Package names for a given facet are delimited by ";". - */ - private static final String FACET_FILTER_DELIMITER = ";"; - - private final Context mContext; - private final CarNavigationBarView mNavBar; - private final CarStatusBar mStatusBar; - - /** - * Set of categories each facet will filter on. - */ - private final List<String[]> mFacetCategories = new ArrayList<>(); - - /** - * Set of package names each facet will filter on. - */ - private final List<String[]> mFacetPackages = new ArrayList<>(); - - private final SimpleArrayMap<String, Integer> mFacetCategoryMap = new SimpleArrayMap<>(); - private final SimpleArrayMap<String, Integer> mFacetPackageMap = new SimpleArrayMap<>(); - - private final List<CarNavigationButton> mNavButtons = new ArrayList<>(); - - private final SparseBooleanArray mFacetHasMultipleAppsCache = new SparseBooleanArray(); - - private int mCurrentFacetIndex; - private Intent mPersistentTaskIntent; - - public CarNavigationBarController(Context context, CarNavigationBarView navBar, - CarStatusBar activityStarter) { - mContext = context; - mNavBar = navBar; - mStatusBar = activityStarter; - bind(); - - if (context.getResources().getBoolean(R.bool.config_enablePersistentDockedActivity)) { - setupPersistentDockedTask(); - } - } - - private void setupPersistentDockedTask() { - try { - mPersistentTaskIntent = Intent.parseUri( - mContext.getString(R.string.config_persistentDockedActivityIntentUri), - Intent.URI_INTENT_SCHEME); - } catch (URISyntaxException e) { - Log.e(TAG, "Malformed persistent task intent."); - } - } - - public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) { - // If the package name belongs to a filter, then highlight appropriate button in - // the navigation bar. - if (mFacetPackageMap.containsKey(packageName)) { - setCurrentFacet(mFacetPackageMap.get(packageName)); - } - - // Check if the package matches any of the categories for the facets - String category = getPackageCategory(packageName); - if (category != null) { - setCurrentFacet(mFacetCategoryMap.get(category)); - } - - // Set up the persistent docked task if needed. - boolean isHomeTask = - taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME; - if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) { - mStatusBar.startActivityOnStack(mPersistentTaskIntent, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); - } - } - - public void onPackageChange(String packageName) { - if (mFacetPackageMap.containsKey(packageName)) { - int index = mFacetPackageMap.get(packageName); - mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index)); - // No need to check categories because we've already refreshed the cache. - return; - } - - String category = getPackageCategory(packageName); - if (mFacetCategoryMap.containsKey(category)) { - int index = mFacetCategoryMap.get(category); - mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index)); - } - } - - /** - * Iterates through the items in arrays_car.xml and sets up the facet bar buttons to - * perform the task in that configuration file when clicked or long-pressed. - */ - private void bind() { - Resources res = mContext.getResources(); - - TypedArray icons = res.obtainTypedArray(R.array.car_facet_icons); - TypedArray intents = res.obtainTypedArray(R.array.car_facet_intent_uris); - TypedArray longPressIntents = res.obtainTypedArray(R.array.car_facet_longpress_intent_uris); - TypedArray facetPackageNames = res.obtainTypedArray(R.array.car_facet_package_filters); - TypedArray facetCategories = res.obtainTypedArray(R.array.car_facet_category_filters); - - try { - if (icons.length() != intents.length() - || icons.length() != longPressIntents.length() - || icons.length() != facetPackageNames.length() - || icons.length() != facetCategories.length()) { - throw new RuntimeException("car_facet array lengths do not match"); - } - - for (int i = 0, size = icons.length(); i < size; i++) { - Drawable icon = icons.getDrawable(i); - CarNavigationButton button = createNavButton(icon); - initClickListeners(button, i, intents.getString(i), longPressIntents.getString(i)); - - mNavButtons.add(button); - mNavBar.addButton(button, createNavButton(icon) /* lightsOutButton */); - - initFacetFilterMaps(i, facetPackageNames.getString(i).split(FACET_FILTER_DELIMITER), - facetCategories.getString(i).split(FACET_FILTER_DELIMITER)); - mFacetHasMultipleAppsCache.put(i, facetHasMultiplePackages(i)); - } - } finally { - // Clean up all the TypedArrays. - icons.recycle(); - intents.recycle(); - longPressIntents.recycle(); - facetPackageNames.recycle(); - facetCategories.recycle(); - } - } - - /** - * Recreates each of the buttons on a density or font scale change. This manual process is - * necessary since this class is not part of an activity that automatically gets recreated. - */ - public void onDensityOrFontScaleChanged() { - TypedArray icons = mContext.getResources().obtainTypedArray(R.array.car_facet_icons); - - try { - int length = icons.length(); - if (length != mNavButtons.size()) { - // This should not happen since the mNavButtons list is created from the length - // of the icons array in bind(). - throw new RuntimeException("car_facet array lengths do not match number of " - + "created buttons."); - } - - for (int i = 0; i < length; i++) { - Drawable icon = icons.getDrawable(i); - - // Setting a new icon will trigger a requestLayout() call if necessary. - mNavButtons.get(i).setResources(icon); - } - } finally { - icons.recycle(); - } - } - - private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) { - mFacetCategories.add(categories); - for (String category : categories) { - mFacetCategoryMap.put(category, id); - } - - mFacetPackages.add(packageNames); - for (String packageName : packageNames) { - mFacetPackageMap.put(packageName, id); - } - } - - private String getPackageCategory(String packageName) { - PackageManager pm = mContext.getPackageManager(); - int size = mFacetCategories.size(); - // For each facet, check if the given package name matches one of its categories - for (int i = 0; i < size; i++) { - String[] categories = mFacetCategories.get(i); - for (int j = 0; j < categories.length; j++) { - String category = categories[j]; - Intent intent = new Intent(); - intent.setPackage(packageName); - intent.setAction(Intent.ACTION_MAIN); - intent.addCategory(category); - List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); - if (list.size() > 0) { - // Cache this package name into facetPackageMap, so we won't have to query - // all categories next time this package name shows up. - mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category)); - return category; - } - } - } - return null; - } - - /** - * Helper method to check if a given facet has multiple packages associated with it. This can - * be resource defined package names or package names filtered by facet category. - * - * @return {@code true} if the facet at the given index has more than one package. - */ - private boolean facetHasMultiplePackages(int index) { - PackageManager pm = mContext.getPackageManager(); - - // Check if the packages defined for the filter actually exists on the device - String[] packages = mFacetPackages.get(index); - if (packages.length > 1) { - int count = 0; - for (int i = 0; i < packages.length; i++) { - count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0; - if (count > 1) { - return true; - } - } - } - - // If there weren't multiple packages defined for the facet, check the categories - // and see if they resolve to multiple package names - String categories[] = mFacetCategories.get(index); - - int count = 0; - for (int i = 0; i < categories.length; i++) { - String category = categories[i]; - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_MAIN); - intent.addCategory(category); - count += pm.queryIntentActivities(intent, 0).size(); - if (count > 1) { - return true; - } - } - return false; - } - - /** - * Sets the facet at the given index to be the facet that is currently active. The button will - * be highlighted appropriately. - */ - private void setCurrentFacet(int index) { - if (index == mCurrentFacetIndex) { - return; - } - - if (mNavButtons.get(mCurrentFacetIndex) != null) { - mNavButtons.get(mCurrentFacetIndex) - .setSelected(false /* selected */, false /* showMoreIcon */); - } - - if (mNavButtons.get(index) != null) { - mNavButtons.get(index).setSelected(true /* selected */, - mFacetHasMultipleAppsCache.get(index) /* showMoreIcon */); - } - - mCurrentFacetIndex = index; - } - - /** - * Creates the View that is used for the buttons along the navigation bar. - * - * @param icon The icon to be used for the button. - */ - private CarNavigationButton createNavButton(Drawable icon) { - CarNavigationButton button = (CarNavigationButton) View.inflate(mContext, - R.layout.car_navigation_button, null); - button.setResources(icon); - LinearLayout.LayoutParams lp = - new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1); - button.setLayoutParams(lp); - - return button; - } - - /** - * Initializes the click and long click listeners that correspond to the given command string. - * The click listeners are attached to the given button. - */ - private void initClickListeners(View button, int index, String clickString, - String longPressString) { - // Each button at least have an action when pressed. - if (TextUtils.isEmpty(clickString)) { - throw new RuntimeException("Facet at index " + index + " does not have click action."); - } - - try { - Intent intent = Intent.parseUri(clickString, Intent.URI_INTENT_SCHEME); - button.setOnClickListener(v -> onFacetClicked(intent, index)); - } catch (URISyntaxException e) { - throw new RuntimeException("Malformed intent uri", e); - } - - if (TextUtils.isEmpty(longPressString)) { - button.setLongClickable(false); - return; - } - - try { - Intent intent = Intent.parseUri(longPressString, Intent.URI_INTENT_SCHEME); - button.setOnLongClickListener(v -> { - onFacetLongClicked(intent, index); - return true; - }); - } catch (URISyntaxException e) { - throw new RuntimeException("Malformed long-press intent uri", e); - } - } - - /** - * Handles a click on a facet. A click will trigger the given Intent. - * - * @param index The index of the facet that was clicked. - */ - private void onFacetClicked(Intent intent, int index) { - String packageName = intent.getPackage(); - - if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) { - return; - } - - intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index)); - intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index)); - // The facet is identified by the index in which it was added to the nav bar. - // This value can be used to determine which facet was selected - intent.putExtra(EXTRA_FACET_ID, Integer.toString(index)); - - // If the current facet is clicked, we want to launch the picker by default - // rather than the "preferred/last run" app. - intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex); - - int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; - int activityType = ACTIVITY_TYPE_UNDEFINED; - if (intent.getCategories().contains(Intent.CATEGORY_HOME)) { - windowingMode = WINDOWING_MODE_UNDEFINED; - activityType = ACTIVITY_TYPE_HOME; - } - - setCurrentFacet(index); - mStatusBar.startActivityOnStack(intent, windowingMode, activityType); - } - - /** - * Handles a long-press on a facet. The long-press will trigger the given Intent. - * - * @param index The index of the facet that was clicked. - */ - private void onFacetLongClicked(Intent intent, int index) { - setCurrentFacet(index); - mStatusBar.startActivityOnStack(intent, - WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index e5a311d099d5..1d9ef616d98d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java @@ -16,17 +16,15 @@ package com.android.systemui.statusbar.car; +import android.app.UiModeManager; import android.content.Context; -import android.graphics.Canvas; import android.util.AttributeSet; -import android.view.MotionEvent; +import android.util.Log; import android.view.View; import android.widget.LinearLayout; +import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; -import com.android.systemui.plugins.statusbar.phone.NavGesture; -import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; -import com.android.systemui.statusbar.phone.NavigationBarView; /** * A custom navigation bar for the automotive use case. @@ -34,9 +32,10 @@ import com.android.systemui.statusbar.phone.NavigationBarView; * The navigation bar in the automotive use case is more like a list of shortcuts, rendered * in a linear layout. */ -class CarNavigationBarView extends NavigationBarView { +class CarNavigationBarView extends LinearLayout { private LinearLayout mNavButtons; - private LinearLayout mLightsOutButtons; + private AlphaOptimizedImageButton mNotificationsButton; + private CarStatusBar mCarStatusBar; public CarNavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -45,99 +44,16 @@ class CarNavigationBarView extends NavigationBarView { @Override public void onFinishInflate() { mNavButtons = findViewById(R.id.nav_buttons); - mLightsOutButtons = findViewById(R.id.lights_out); - } - public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){ - mNavButtons.addView(button); - mLightsOutButtons.addView(lightsOutButton); + mNotificationsButton = findViewById(R.id.notifications); + mNotificationsButton.setOnClickListener(this::onNotificationsClick); } - @Override - public void setDisabledFlags(int disabledFlags, boolean force) { - // TODO: Populate. + void setStatusBar(CarStatusBar carStatusBar) { + mCarStatusBar = carStatusBar; } - @Override - public void reorient() { - // We expect the car head unit to always have a fixed rotation so we ignore this. The super - // class implentation expects mRotatedViews to be populated, so if you call into it, there - // is a possibility of a NullPointerException. - } - - @Override - public View getCurrentView() { - return this; - } - - @Override - public void setNavigationIconHints(int hints, boolean force) { - // We do not need to set the navigation icon hints for a vehicle - // Calling setNavigationIconHints in the base class will result in a NPE as the car - // navigation bar does not have a back button. - } - - @Override - public void onPluginConnected(NavGesture plugin, Context context) { - // set to null version of the plugin ignoring incoming arg. - super.onPluginConnected(new NullNavGesture(), context); - } - - @Override - public void onPluginDisconnected(NavGesture plugin) { - // reinstall the null nav gesture plugin - super.onPluginConnected(new NullNavGesture(), getContext()); - } - - /** - * Null object pattern to work around expectations of the base class. - * This is a temporary solution to have the car system ui working. - * Already underway is a refactor of they car sys ui as to not use this class - * hierarchy. - */ - private static class NullNavGesture implements NavGesture { - @Override - public GestureHelper getGestureHelper() { - return new GestureHelper() { - @Override - public boolean onTouchEvent(MotionEvent event) { - return false; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - return false; - } - - @Override - public void setBarState(boolean vertical, boolean isRtl) { - } - - @Override - public void onDraw(Canvas canvas) { - } - - @Override - public void onDarkIntensityChange(float intensity) { - } - - @Override - public void onLayout(boolean changed, int left, int top, int right, int bottom) { - } - }; - } - - @Override - public int getVersion() { - return 0; - } - - @Override - public void onCreate(Context sysuiContext, Context pluginContext) { - } - - @Override - public void onDestroy() { - } + protected void onNotificationsClick(View v) { + mCarStatusBar.togglePanel(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java index 2de358f1c292..0cdaec1432c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -1,72 +1,87 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.android.systemui.statusbar.car; import android.content.Context; -import android.graphics.drawable.Drawable; +import android.content.Intent; +import android.content.res.TypedArray; import android.util.AttributeSet; import android.widget.ImageView; -import android.widget.RelativeLayout; -import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; +import java.net.URISyntaxException; + /** - * A wrapper view for a car navigation facet, which includes a button icon and a drop down icon. + * CarNavigationButton is an image button that allows for a bit more configuration at the + * xml file level. This allows for more control via overlays instead of having to update + * code. */ -public class CarNavigationButton extends RelativeLayout { +public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton { + private static final float SELECTED_ALPHA = 1; private static final float UNSELECTED_ALPHA = 0.7f; - private AlphaOptimizedImageButton mIcon; - private AlphaOptimizedImageButton mMoreIcon; + private Context mContext; + private String mIntent = null; + private String mLongIntent = null; + private boolean mBroadcastIntent = false; + private boolean mSelected = false; + public CarNavigationButton(Context context, AttributeSet attrs) { super(context, attrs); + mContext = context; + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarNavigationButton); + mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent); + mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent); + mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false); } + + /** + * After the standard inflate this then adds the xml defined intents to click and long click + * actions if defined. + */ @Override public void onFinishInflate() { super.onFinishInflate(); - mIcon = findViewById(R.id.car_nav_button_icon); - mIcon.setScaleType(ImageView.ScaleType.CENTER); - mIcon.setClickable(false); - mIcon.setBackgroundColor(android.R.color.transparent); - mIcon.setAlpha(UNSELECTED_ALPHA); - - mMoreIcon = findViewById(R.id.car_nav_button_more_icon); - mMoreIcon.setClickable(false); - mMoreIcon.setBackgroundColor(android.R.color.transparent); - mMoreIcon.setVisibility(INVISIBLE); - mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow)); - mMoreIcon.setAlpha(UNSELECTED_ALPHA); - } + setScaleType(ImageView.ScaleType.CENTER); + setAlpha(UNSELECTED_ALPHA); + try { + if (mIntent != null) { + final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + setOnClickListener(v -> { + if (mBroadcastIntent) { + mContext.sendBroadcast(intent); + return; + } + mContext.startActivity(intent); + }); + } + } catch (URISyntaxException e) { + throw new RuntimeException("Failed to attach intent", e); + } - public void setResources(Drawable icon) { - mIcon.setImageDrawable(icon); + try { + if (mLongIntent != null) { + final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + setOnLongClickListener(v -> { + mContext.startActivity(intent); + return true; + }); + } + } catch (URISyntaxException e) { + throw new RuntimeException("Failed to attach long press intent", e); + } } - public void setSelected(boolean selected, boolean showMoreIcon) { - if (selected) { - mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE); - mMoreIcon.setAlpha(SELECTED_ALPHA); - mIcon.setAlpha(SELECTED_ALPHA); - } else { - mMoreIcon.setVisibility(INVISIBLE); - mIcon.setAlpha(UNSELECTED_ALPHA); - } + /** + * @param selected true if should indicate if this is a selected state, false otherwise + */ + public void setSelected(boolean selected) { + super.setSelected(selected); + mSelected = selected; + setAlpha(mSelected ? SELECTED_ALPHA : UNSELECTED_ALPHA); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 3dfb9130af2e..c15a01330534 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -18,17 +18,14 @@ package com.android.systemui.statusbar.car; import android.app.ActivityManager; import android.app.ActivityOptions; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import android.util.Log; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; @@ -46,10 +43,7 @@ import com.android.systemui.classifier.FalsingManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.statusbar.ExpandableNotificationRow; -import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.NavigationBarView; @@ -69,7 +63,6 @@ public class CarStatusBar extends StatusBar implements private TaskStackListenerImpl mTaskStackListener; - private CarNavigationBarController mController; private FullscreenUserSwitcher mFullscreenUserSwitcher; private CarBatteryController mCarBatteryController; @@ -78,15 +71,23 @@ public class CarStatusBar extends StatusBar implements private ConnectedDeviceSignalController mConnectedDeviceSignalController; private ViewGroup mNavigationBarWindow; + private ViewGroup mLeftNavigationBarWindow; + private ViewGroup mRightNavigationBarWindow; private CarNavigationBarView mNavigationBarView; + private CarNavigationBarView mLeftNavigationBarView; + private CarNavigationBarView mRightNavigationBarView; private final Object mQueueLock = new Object(); + private boolean mShowLeft; + private boolean mShowRight; + private boolean mShowBottom; + private CarFacetButtonController mCarFacetButtonController; + @Override public void start() { super.start(); mTaskStackListener = new TaskStackListenerImpl(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - registerPackageChangeReceivers(); mStackScroller.setScrollingEnabled(true); @@ -104,6 +105,16 @@ public class CarStatusBar extends StatusBar implements mNavigationBarView = null; } + if (mLeftNavigationBarWindow != null) { + mWindowManager.removeViewImmediate(mLeftNavigationBarWindow); + mLeftNavigationBarView = null; + } + + if (mRightNavigationBarWindow != null) { + mWindowManager.removeViewImmediate(mRightNavigationBarWindow); + mRightNavigationBarView = null; + } + super.destroy(); } @@ -153,10 +164,36 @@ public class CarStatusBar extends StatusBar implements @Override protected void createNavigationBar() { + mCarFacetButtonController = new CarFacetButtonController(mContext); if (mNavigationBarView != null) { return; } + mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar); + if (mShowBottom) { + buildBottomBar(); + } + + int widthForSides = mContext.getResources().getDimensionPixelSize( + R.dimen.navigation_bar_height_car_mode); + + + mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); + + if (mShowLeft) { + buildLeft(widthForSides); + } + + mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); + + if (mShowRight) { + buildRight(widthForSides); + } + + } + + + private void buildBottomBar() { // SystemUI requires that the navigation bar view have a parent. Since the regular // StatusBar inflates navigation_bar_window as this parent view, use the same view for the // CarNavigationBarView. @@ -171,17 +208,15 @@ public class CarStatusBar extends StatusBar implements mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0); if (mNavigationBarView == null) { Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + throw new RuntimeException("Unable to build botom nav bar due to missing layout"); } + mNavigationBarView.setStatusBar(this); - mController = new CarNavigationBarController(mContext, mNavigationBarView, - this /* ActivityStarter*/); - mNavigationBarView.getBarTransitions().setAlwaysOpaque(true); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, @@ -189,9 +224,74 @@ public class CarStatusBar extends StatusBar implements lp.setTitle("CarNavigationBar"); lp.windowAnimations = 0; + + mCarFacetButtonController.addCarNavigationBar(mNavigationBarView); mWindowManager.addView(mNavigationBarWindow, lp); } + private void buildLeft(int widthForSides) { + mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); + if (mLeftNavigationBarWindow == null) { + Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window"); + } + + View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow); + mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0); + if (mLeftNavigationBarView == null) { + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + throw new RuntimeException("Unable to build left nav bar due to missing layout"); + } + mLeftNavigationBarView.setStatusBar(this); + mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView); + + WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( + widthForSides, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + leftlp.setTitle("LeftCarNavigationBar"); + leftlp.windowAnimations = 0; + leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; + leftlp.gravity = Gravity.LEFT; + mWindowManager.addView(mLeftNavigationBarWindow, leftlp); + } + + + private void buildRight(int widthForSides) { + mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); + if (mRightNavigationBarWindow == null) { + Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window"); + } + + View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow); + mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0); + if (mRightNavigationBarView == null) { + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + throw new RuntimeException("Unable to build right nav bar due to missing layout"); + } + mRightNavigationBarView.setStatusBar(this); + mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView); + + WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( + widthForSides, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + rightlp.setTitle("RightCarNavigationBar"); + rightlp.windowAnimations = 0; + rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; + rightlp.gravity = Gravity.RIGHT; + mWindowManager.addView(mRightNavigationBarWindow, rightlp); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { //When executing dump() funciton simultaneously, we need to serialize them @@ -204,8 +304,8 @@ public class CarStatusBar extends StatusBar implements } pw.print(" mTaskStackListener="); pw.println(mTaskStackListener); - pw.print(" mController="); - pw.println(mController); + pw.print(" mCarFacetButtonController="); + pw.println(mCarFacetButtonController); pw.print(" mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher); pw.print(" mCarBatteryController="); pw.println(mCarBatteryController); @@ -229,10 +329,6 @@ public class CarStatusBar extends StatusBar implements } } - @Override - public NavigationBarView getNavigationBarView() { - return mNavigationBarView; - } @Override public View getNavigationBarWindow() { @@ -269,24 +365,6 @@ public class CarStatusBar extends StatusBar implements } } - private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getData() == null || mController == null) { - return; - } - String packageName = intent.getData().getSchemeSpecificPart(); - mController.onPackageChange(packageName); - } - }; - - private void registerPackageChangeReceivers() { - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addDataScheme("package"); - mContext.registerReceiver(mPackageChangeReceiver, filter); - } public boolean hasDockedTask() { return Recents.getSystemServices().hasDockedTask(); @@ -301,10 +379,7 @@ public class CarStatusBar extends StatusBar implements public void onTaskStackChanged() { ActivityManager.RunningTaskInfo runningTaskInfo = ActivityManagerWrapper.getInstance().getRunningTask(); - if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) { - mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(), - runningTaskInfo); - } + mCarFacetButtonController.taskChanged(runningTaskInfo); } } @@ -346,33 +421,6 @@ public class CarStatusBar extends StatusBar implements // Do nothing, we don't want to display media art in the lock screen for a car. } - private int startActivityWithOptions(Intent intent, Bundle options) { - int result = ActivityManager.START_CANCELED; - try { - result = ActivityManager.getService().startActivityAsUser(null /* caller */, - mContext.getBasePackageName(), - intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - null /* resultTo*/, - null /* resultWho*/, - 0 /* requestCode*/, - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP, - null /* profilerInfo*/, - options, - UserHandle.CURRENT.getIdentifier()); - } catch (RemoteException e) { - Log.w(TAG, "Unable to start activity", e); - } - - return result; - } - - public int startActivityOnStack(Intent intent, int windowingMode, int activityType) { - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchWindowingMode(windowingMode); - options.setLaunchActivityType(activityType); - return startActivityWithOptions(intent, options.toBundle()); - } @Override public void animateExpandNotificationsPanel() { @@ -390,8 +438,6 @@ public class CarStatusBar extends StatusBar implements @Override public void onDensityOrFontScaleChanged() { super.onDensityOrFontScaleChanged(); - mController.onDensityOrFontScaleChanged(); - // Need to update the background on density changed in case the change was due to night // mode. mNotificationPanelBackground = getDefaultWallpaper(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index cd4eb236c892..c047670f95db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -403,14 +403,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) { + final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled(); if (oldConfig.orientation != newConfig.orientation || oldConfig.densityDpi != newConfig.densityDpi) { mDockedIcon = getDrawable(ctx, R.drawable.ic_sysbar_docked, R.drawable.ic_sysbar_docked_dark); + mHomeDefaultIcon = quickStepEnabled + ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step, + R.drawable.ic_sysbar_home_quick_step_dark) + : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark); } if (oldConfig.densityDpi != newConfig.densityDpi || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) { - final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled(); mBackIcon = quickStepEnabled ? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step, R.drawable.ic_sysbar_back_quick_step_dark) @@ -422,11 +426,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav : getDrawable(ctx, R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark); mBackAltLandIcon = mBackAltIcon; - - mHomeDefaultIcon = quickStepEnabled - ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step, - R.drawable.ic_sysbar_home_quick_step_dark) - : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark); mRecentIcon = getDrawable(ctx, R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark); mMenuIcon = getDrawable(ctx, R.drawable.ic_sysbar_menu, R.drawable.ic_sysbar_menu_dark); @@ -909,6 +908,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav Log.d(TAG, "reorient(): rot=" + mCurrentRotation); } + // Resolve layout direction if not resolved since components changing layout direction such + // as changing languages will recreate this view and the direction will be resolved later + if (!isLayoutDirectionResolved()) { + resolveLayoutDirection(); + } updateTaskSwitchHelper(); setNavigationIconHints(mNavigationIconHints, true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 6444cc816663..747a551defe6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -575,7 +575,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, Intent browserIntent = getTaskIntent(taskId, userId); Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL); - if (browserIntent != null) { + if (browserIntent != null && browserIntent.isWebIntent()) { // Make sure that this doesn't resolve back to an instant app browserIntent.setComponent(null) .setPackage(null) @@ -597,8 +597,9 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, .addCategory("unique:" + System.currentTimeMillis()) .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName) .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff)) - .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode) - .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent); + .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode) + .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent) + .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent); PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0); Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java index 0bf01b0cb949..378858a9b816 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java @@ -224,6 +224,10 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene case MotionEvent.ACTION_DOWN: { int x = (int) event.getX(); int y = (int) event.getY(); + // End any existing quickscrub animations before starting the new transition + if (mQuickScrubEndAnimator != null) { + mQuickScrubEndAnimator.end(); + } mHomeButtonView = homeButton.getCurrentView(); if (mNavigationBarView.isQuickScrubEnabled() && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 1c9c7949a971..676463407f3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -37,6 +37,7 @@ import static java.lang.Thread.sleep; import android.content.Intent; import android.metrics.LogMaker; import android.support.test.filters.SmallTest; +import android.support.test.InstrumentationRegistry; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -73,6 +74,7 @@ public class QSTileImplTest extends SysuiTestCase { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mHost = mock(QSTileHost.class); when(mHost.indexOf(spec)).thenReturn(POSITION); + when(mHost.getContext()).thenReturn(mContext.getBaseContext()); mTile = spy(new TileImpl(mHost)); mTile.mHandler = mTile.new H(mTestableLooper.getLooper()); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 320c37fdc319..abf1de578682 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5257,6 +5257,23 @@ message MetricsEvent { // OS: P FIELD_END_BATTERY_PERCENT = 1308; + // ACTION: Settings > Display > Night Light + // SUBTYPE: com.android.server.display.ColorDisplayService.AutoMode value + // CATEGORY: SETTINGS + // OS: P + ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED = 1309; + + // ACTION: Settings > Display > Night Light + // CATEGORY: SETTINGS + // SUBTYPE: 0 is starting time, 1 is ending time + // OS: P + ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED = 1310; + + // FIELD: Current mode corresponding to a QS tile + // CATEGORY: QUICK SETTINGS + // OS: P + FIELD_QS_MODE = 1311; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/rs/OWNERS b/rs/OWNERS new file mode 100644 index 000000000000..61853d3d40cf --- /dev/null +++ b/rs/OWNERS @@ -0,0 +1,5 @@ +butlermichael@google.com +dgross@google.com +jeanluc@google.com +miaowang@google.com +yangni@google.com diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 8240e4b758da..c50446514141 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -24,6 +24,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.PackageManager; import android.graphics.Point; import android.graphics.Rect; import android.service.autofill.Dataset; @@ -37,6 +38,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.view.autofill.AutofillId; @@ -98,22 +100,28 @@ final class FillUi { private @Nullable AnnounceFilterResult mAnnounceFilterResult; + private final boolean mFullScreen; private int mContentWidth; private int mContentHeight; private boolean mDestroyed; + public static boolean isFullScreen(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); + } + FillUi(@NonNull Context context, @NonNull FillResponse response, - @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText, - @NonNull OverlayControl overlayControl, @NonNull Callback callback) { + @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText, + @NonNull OverlayControl overlayControl, @NonNull Callback callback) { mContext = context; mCallback = callback; + mFullScreen = isFullScreen(context); final LayoutInflater inflater = LayoutInflater.from(context); final ViewGroup decor = (ViewGroup) inflater.inflate( - R.layout.autofill_dataset_picker, null); - + mFullScreen ? R.layout.autofill_dataset_picker_fullscreen + : R.layout.autofill_dataset_picker, null); final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() { @Override @@ -130,31 +138,41 @@ final class FillUi { mListView = null; mAdapter = null; + // insert authentication item under autofill_dataset_container or decor + ViewGroup container = decor.findViewById(R.id.autofill_dataset_container); + if (container == null) { + container = decor; + } final View content; try { content = response.getPresentation().apply(context, decor, interceptionHandler); - decor.addView(content); + container.addView(content); } catch (RuntimeException e) { callback.onCanceled(); Slog.e(TAG, "Error inflating remote views", e); mWindow = null; return; } + decor.setFocusable(true); + decor.setOnClickListener(v -> mCallback.onResponsePicked(response)); Point maxSize = mTempPoint; resolveMaxWindowSize(context, maxSize); + // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width + content.getLayoutParams().width = mFullScreen ? maxSize.x + : ViewGroup.LayoutParams.WRAP_CONTENT; + content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x, MeasureSpec.AT_MOST); final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y, MeasureSpec.AT_MOST); decor.measure(widthMeasureSpec, heightMeasureSpec); - decor.setOnClickListener(v -> mCallback.onResponsePicked(response)); mContentWidth = content.getMeasuredWidth(); mContentHeight = content.getMeasuredHeight(); mWindow = new AnchoredWindow(decor, overlayControl); - mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter); + requestShowFillUi(); } else { final int datasetCount = response.getDatasets().size(); @@ -261,6 +279,15 @@ final class FillUi { } } + void requestShowFillUi() { + if (mFullScreen) { + mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + mWindowPresenter); + } else { + mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter); + } + } + /** * Creates a remoteview interceptor used to block clicks. */ @@ -289,7 +316,13 @@ final class FillUi { mCallback.requestHideFillUi(); } else { if (updateContentSize()) { - mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter); + if (mFullScreen) { + LayoutParams lp = mListView.getLayoutParams(); + lp.width = mContentWidth; + lp.height = mContentHeight; + mListView.setLayoutParams(lp); + } + requestShowFillUi(); } if (mAdapter.getCount() > VISIBLE_OPTIONS_MAX_COUNT) { mListView.setVerticalScrollBarEnabled(true); @@ -310,7 +343,7 @@ final class FillUi { // ViewState doesn't not support filtering - typically when it's for an authenticated // FillResponse. if (TextUtils.isEmpty(filterText)) { - mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter); + requestShowFillUi(); } else { mCallback.requestHideFillUi(); } @@ -366,23 +399,39 @@ final class FillUi { final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y, MeasureSpec.AT_MOST); final int itemCount = mAdapter.getCount(); + if (mFullScreen) { + // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width + changed = true; + mContentWidth = maxSize.x; + } for (int i = 0; i < itemCount; i++) { final View view = mAdapter.getItem(i).view; view.measure(widthMeasureSpec, heightMeasureSpec); - final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x); - final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth); - if (newContentWidth != mContentWidth) { - mContentWidth = newContentWidth; - changed = true; - } - // Update the width to fit only the first items up to max count - if (i < VISIBLE_OPTIONS_MAX_COUNT) { - final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y); - final int newContentHeight = mContentHeight + clampedMeasuredHeight; - if (newContentHeight != mContentHeight) { - mContentHeight = newContentHeight; + if (mFullScreen) { + // for fullscreen, add up all children height until hit max height. + final int newContentHeight = mContentHeight + view.getMeasuredHeight(); + final int clampedNewHeight = Math.min(newContentHeight, maxSize.y); + if (clampedNewHeight != mContentHeight) { + mContentHeight = clampedNewHeight; + } else if (view.getMeasuredHeight() > 0) { + break; + } + } else { + final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x); + final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth); + if (newContentWidth != mContentWidth) { + mContentWidth = newContentWidth; changed = true; } + // Update the width to fit only the first items up to max count + if (i < VISIBLE_OPTIONS_MAX_COUNT) { + final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y); + final int newContentHeight = mContentHeight + clampedMeasuredHeight; + if (newContentHeight != mContentHeight) { + mContentHeight = newContentHeight; + changed = true; + } + } } } return changed; @@ -575,6 +624,7 @@ final class FillUi { public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null); + pw.print(prefix); pw.print("mFullScreen: "); pw.println(mFullScreen); pw.print(prefix); pw.print("mListView: "); pw.println(mListView); pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter); pw.print(prefix); pw.print("mFilterText: "); diff --git a/services/backup/OWNERS b/services/backup/OWNERS new file mode 100644 index 000000000000..1c9a43acfa65 --- /dev/null +++ b/services/backup/OWNERS @@ -0,0 +1,7 @@ +artikz@google.com +brufino@google.com +bryanmawhinney@google.com +ctate@google.com +jorlow@google.com +mkarpinski@google.com + diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 0dcefbfd26a4..8205265ba047 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -932,6 +932,7 @@ class ActivityStarter { // Don't modify the client's object! intent = new Intent(intent); if (componentSpecified + && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null) && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction()) && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction()) && mService.getPackageManagerInternalLocked() diff --git a/services/core/java/com/android/server/audio/OWNERS b/services/core/java/com/android/server/audio/OWNERS new file mode 100644 index 000000000000..b70de299eeea --- /dev/null +++ b/services/core/java/com/android/server/audio/OWNERS @@ -0,0 +1,2 @@ +jmtrivi@google.com +elaurent@google.com diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index d87a1bb9f96f..79450a0f85b5 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -2078,8 +2078,33 @@ public class SyncManager { protected void dumpSyncState(PrintWriter pw) { final StringBuilder sb = new StringBuilder(); - pw.print("data connected: "); pw.println(mDataConnectionIsConnected); - pw.print("auto sync: "); + pw.print("Data connected: "); pw.println(mDataConnectionIsConnected); + pw.print("Battery saver: "); + pw.println((mPowerManager != null) && mPowerManager.isPowerSaveMode()); + + pw.print("Background network restriction: "); + { + final ConnectivityManager cm = getConnectivityManager(); + final int status = (cm == null) ? -1 : cm.getRestrictBackgroundStatus(); + switch (status) { + case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED: + pw.println(" disabled"); + break; + case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED: + pw.println(" whitelisted"); + break; + case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED: + pw.println(" enabled"); + break; + default: + pw.print("Unknown("); + pw.print(status); + pw.println(")"); + break; + } + } + + pw.print("Auto sync: "); List<UserInfo> users = getAllUsers(); if (users != null) { for (UserInfo user : users) { @@ -2088,26 +2113,26 @@ public class SyncManager { } pw.println(); } - pw.print("memory low: "); pw.println(mStorageIsLow); - pw.print("device idle: "); pw.println(mDeviceIsIdle); - pw.print("reported active: "); pw.println(mReportedSyncActive); + pw.print("Memory low: "); pw.println(mStorageIsLow); + pw.print("Device idle: "); pw.println(mDeviceIsIdle); + pw.print("Reported active: "); pw.println(mReportedSyncActive); final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); - pw.print("accounts: "); + pw.print("Accounts: "); if (accounts != INITIAL_ACCOUNTS_ARRAY) { pw.println(accounts.length); } else { pw.println("not known yet"); } final long now = SystemClock.elapsedRealtime(); - pw.print("now: "); pw.print(now); + pw.print("Now: "); pw.print(now); pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); sb.setLength(0); - pw.print("uptime: "); pw.print(formatDurationHMS(sb, now)); + pw.print("Uptime: "); pw.print(formatDurationHMS(sb, now)); pw.println(); - pw.print("time spent syncing: "); + pw.print("Time spent syncing: "); sb.setLength(0); pw.print(formatDurationHMS(sb, diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java index 2f35687fa44f..061e4ca02d2d 100644 --- a/services/core/java/com/android/server/content/SyncManagerConstants.java +++ b/services/core/java/com/android/server/content/SyncManagerConstants.java @@ -22,6 +22,8 @@ import android.provider.Settings.Global; import android.util.KeyValueListParser; import android.util.Slog; +import com.android.internal.os.BackgroundThread; + import java.io.PrintWriter; public class SyncManagerConstants extends ContentObserver { @@ -53,13 +55,14 @@ public class SyncManagerConstants extends ContentObserver { protected SyncManagerConstants(Context context) { super(null); mContext = context; - refresh(); } public void start() { - mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( - Settings.Global.SYNC_MANAGER_CONSTANTS), false, this); - refresh(); + BackgroundThread.getHandler().post(() -> { + mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( + Settings.Global.SYNC_MANAGER_CONSTANTS), false, this); + refresh(); + }); } @Override diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 3da35517faf0..692535c6cebc 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -421,7 +421,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death byteToken[i] = token.get(i); } // Send to Keystore - KeyStore.getInstance().addAuthToken(byteToken); + KeyStore.getInstance().addAuthToken(byteToken, mCurrentUserId); } if (client != null && client.onAuthenticated(fingerId, groupId)) { removeClient(client); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index e5f4282eefe0..0cba76ba7346 100644..100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -660,7 +660,8 @@ abstract class HdmiCecLocalDevice { @ServiceThreadOnly void startQueuedActions() { assertRunOnServiceThread(); - for (HdmiCecFeatureAction action : mActions) { + // Use copied action list in that start() may remove itself. + for (HdmiCecFeatureAction action : new ArrayList<>(mActions)) { if (!action.started()) { Slog.i(TAG, "Starting queued action:" + action); action.start(); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index be48f69e1545..c33d7f4b4451 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -23,6 +23,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.AlarmManager; import android.app.AppGlobals; import android.app.IUidObserver; import android.app.job.IJobScheduler; @@ -184,6 +185,7 @@ public final class JobSchedulerService extends com.android.server.SystemService IBatteryStats mBatteryStats; DeviceIdleController.LocalService mLocalDeviceIdleController; AppStateTracker mAppStateTracker; + final UsageStatsManagerInternal mUsageStats; /** * Set to true once we are allowed to run third party apps. @@ -225,7 +227,10 @@ public final class JobSchedulerService extends com.android.server.SystemService */ final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE }; long mHeartbeat = 0; - long mLastHeartbeatTime = 0; + long mLastHeartbeatTime = sElapsedRealtimeClock.millis(); + + static final String HEARTBEAT_TAG = "*job.heartbeat*"; + final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener(); // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- @@ -495,6 +500,9 @@ public final class JobSchedulerService extends com.android.server.SystemService STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS, DEFAULT_STANDBY_RARE_BEATS); } + + // Reset the heartbeat alarm based on the new heartbeat duration + setNextHeartbeatAlarm(); } void dump(PrintWriter pw) { @@ -1090,9 +1098,9 @@ public final class JobSchedulerService extends com.android.server.SystemService mJobSchedulerStub = new JobSchedulerStub(); // Set up the app standby bucketing tracker - UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class); - mStandbyTracker = new StandbyTracker(usageStats); - usageStats.addAppIdleStateChangeListener(mStandbyTracker); + mStandbyTracker = new StandbyTracker(); + mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); + mUsageStats.addAppIdleStateChangeListener(mStandbyTracker); // The job store needs to call back publishLocalService(JobSchedulerInternal.class, new LocalService()); @@ -1177,6 +1185,7 @@ public final class JobSchedulerService extends com.android.server.SystemService mAppStateTracker = Preconditions.checkNotNull( LocalServices.getService(AppStateTracker.class)); + setNextHeartbeatAlarm(); // Register br for package removals and user removals. final IntentFilter filter = new IntentFilter(); @@ -1418,6 +1427,23 @@ public final class JobSchedulerService extends com.android.server.SystemService periodicToReschedule.getLastFailedRunTime()); } + long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) { + final long heartbeat; + final long timeSinceLastJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId); + synchronized (mLock) { + heartbeat = mHeartbeat - (timeSinceLastJob / mConstants.STANDBY_HEARTBEAT_TIME); + } + if (DEBUG_STANDBY) { + Slog.v(TAG, "Last job heartbeat " + heartbeat + " for " + packageName + "/" + userId + + " delta=" + timeSinceLastJob); + } + return heartbeat; + } + + long heartbeatWhenJobsLastRun(JobStatus job) { + return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId()); + } + // JobCompletedListener implementations. /** @@ -1560,9 +1586,7 @@ public final class JobSchedulerService extends com.android.server.SystemService noteJobsNonpending(mPendingJobs); mPendingJobs.clear(); stopNonReadyActiveJobsLocked(); - boolean updated = updateStandbyHeartbeatLocked(); mJobs.forEachJob(mReadyQueueFunctor); - if (updated) updateNextStandbyHeartbeatsLocked(); mReadyQueueFunctor.postProcess(); if (DEBUG) { @@ -1716,38 +1740,80 @@ public final class JobSchedulerService extends com.android.server.SystemService noteJobsNonpending(mPendingJobs); mPendingJobs.clear(); stopNonReadyActiveJobsLocked(); - boolean updated = updateStandbyHeartbeatLocked(); mJobs.forEachJob(mMaybeQueueFunctor); - if (updated) updateNextStandbyHeartbeatsLocked(); mMaybeQueueFunctor.postProcess(); } - private boolean updateStandbyHeartbeatLocked() { - final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime; - final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME; - if (beatsElapsed > 0) { - mHeartbeat += beatsElapsed; - mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME; - if (DEBUG_STANDBY) { - Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed + " to " + mHeartbeat); + /** + * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup. + */ + class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener { + + @Override + public void onAlarm() { + synchronized (mLock) { + final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime; + final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME; + if (beatsElapsed > 0) { + mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME; + advanceHeartbeatLocked(beatsElapsed); + } } - return true; + setNextHeartbeatAlarm(); } - return false; } - private void updateNextStandbyHeartbeatsLocked() { - // don't update ACTIVE or NEVER bucket milestones + // Intentionally does not touch the alarm timing + void advanceHeartbeatLocked(long beatsElapsed) { + mHeartbeat += beatsElapsed; + if (DEBUG_STANDBY) { + Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed + + " to " + mHeartbeat); + } + // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat + // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which + // new jobs scheduled by apps in that bucket will be permitted to run + // immediately. + boolean didAdvanceBucket = false; for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) { - while (mHeartbeat >= mNextBucketHeartbeat[i]) { + // Did we reach or cross a bucket boundary? + if (mHeartbeat >= mNextBucketHeartbeat[i]) { + didAdvanceBucket = true; + } + while (mHeartbeat > mNextBucketHeartbeat[i]) { mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i]; } if (DEBUG_STANDBY) { - Slog.v(TAG, " Bucket " + i + " next heartbeat " + mNextBucketHeartbeat[i]); + Slog.v(TAG, " Bucket " + i + " next heartbeat " + + mNextBucketHeartbeat[i]); + } + } + + if (didAdvanceBucket) { + if (DEBUG_STANDBY) { + Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability"); } + mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } } + void setNextHeartbeatAlarm() { + final long heartbeatLength; + synchronized (mLock) { + heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME; + } + final long now = sElapsedRealtimeClock.millis(); + final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength; + final long nextHeartbeat = nextBeatOrdinal * heartbeatLength; + if (DEBUG_STANDBY) { + Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat + + " = " + TimeUtils.formatDuration(nextHeartbeat - now)); + } + AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); + am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat, + HEARTBEAT_TAG, mHeartbeatAlarm, mHandler); + } + /** * Criteria for moving a job into the pending queue: * - It's ready. @@ -1811,17 +1877,20 @@ public final class JobSchedulerService extends com.android.server.SystemService if (!mInParole && !job.getJob().isExemptedFromAppStandby()) { final int bucket = job.getStandbyBucket(); if (mHeartbeat < mNextBucketHeartbeat[bucket]) { - // Only skip this job if it's still waiting for the end of its (initial) nominal + // Only skip this job if the app is still waiting for the end of its nominal // bucket interval. Once it's waited that long, we let it go ahead and clear. // The final (NEVER) bucket is special; we never age those apps' jobs into // runnability. + final long appLastRan = heartbeatWhenJobsLastRun(job); if (bucket >= mConstants.STANDBY_BEATS.length - || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) { + || (mHeartbeat > appLastRan + && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) { // TODO: log/trace that we're deferring the job due to bucketing if we hit this if (job.getWhenStandbyDeferred() == 0) { if (DEBUG_STANDBY) { Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " - + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job); + + (appLastRan + mConstants.STANDBY_BEATS[bucket]) + + " for " + job); } job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); } @@ -2078,18 +2147,19 @@ public final class JobSchedulerService extends com.android.server.SystemService // ACTIVE => everything can be run right away // NEVER => we won't run them anyway, so let them go in the future // as soon as the app enters normal use + if (DEBUG_STANDBY) { + Slog.v(TAG, "Base heartbeat forced ZERO for new job in " + + packageName + "/" + userId); + } return 0; } - final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun( - packageName, userId); - final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket]; - final long bucketsAgo = timeSinceLastJob / bucketLength; - - // If we haven't run any jobs for more than the app's current bucket period, just - // consider anything new to be immediately runnable. Otherwise, base it on the - // bucket at which we last ran jobs. - return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo); + final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId); + if (DEBUG_STANDBY) { + Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in " + + packageName + "/" + userId); + } + return baseHeartbeat; } /** @@ -2166,15 +2236,6 @@ public final class JobSchedulerService extends com.android.server.SystemService * Tracking of app assignments to standby buckets */ final class StandbyTracker extends AppIdleStateChangeListener { - final UsageStatsManagerInternal mUsageStats; - - StandbyTracker(UsageStatsManagerInternal usageStats) { - mUsageStats = usageStats; - } - - public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) { - return mUsageStats.getTimeSinceLastJobRun(packageName, userId); - } // AppIdleStateChangeListener interface for live updates @@ -2256,6 +2317,7 @@ public final class JobSchedulerService extends com.android.server.SystemService else return 0; } + // Static to support external callers public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) { UsageStatsManagerInternal usageStats = LocalServices.getService( UsageStatsManagerInternal.class); @@ -2682,6 +2744,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + // Shell command infrastructure int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) { try { final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, @@ -2759,6 +2822,21 @@ public final class JobSchedulerService extends com.android.server.SystemService return 0; } + // Shell command infrastructure + int executeHeartbeatCommand(PrintWriter pw, int numBeats) { + if (numBeats < 1) { + pw.println(getCurrentHeartbeat()); + return 0; + } + + pw.print("Advancing standby heartbeat by "); + pw.println(numBeats); + synchronized (mLock) { + advanceHeartbeatLocked(numBeats); + } + return 0; + } + private String printContextIdToJobMap(JobStatus[] map, String initial) { StringBuilder s = new StringBuilder(initial + ": "); for (int i=0; i<map.length; i++) { diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java index d630aab61ce5..63225f34234f 100644 --- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java @@ -64,6 +64,8 @@ public final class JobSchedulerShellCommand extends ShellCommand { return getStorageNotLow(pw); case "get-job-state": return getJobState(pw); + case "heartbeat": + return doHeartbeat(pw); default: return handleDefaultCommands(cmd); } @@ -333,6 +335,20 @@ public final class JobSchedulerShellCommand extends ShellCommand { } } + private int doHeartbeat(PrintWriter pw) throws Exception { + checkPermission("manipulate scheduler heartbeat"); + + final String arg = getNextArg(); + final int numBeats = (arg != null) ? Integer.parseInt(arg) : 0; + + final long ident = Binder.clearCallingIdentity(); + try { + return mInternal.executeHeartbeatCommand(pw, numBeats); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -359,6 +375,9 @@ public final class JobSchedulerShellCommand extends ShellCommand { pw.println(" Options:"); pw.println(" -u or --user: specify which user's job is to be run; the default is"); pw.println(" the primary or system user"); + pw.println(" heartbeat [num]"); + pw.println(" With no argument, prints the current standby heartbeat. With a positive"); + pw.println(" argument, advances the standby heartbeat by that number."); pw.println(" monitor-battery [on|off]"); pw.println(" Control monitoring of all battery changes. Off by default. Turning"); pw.println(" on makes get-battery-seq useful."); diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index 4988974e95db..1f8cf769ab98 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -240,11 +240,6 @@ public final class JobServiceContext implements ServiceConnection { } } - UsageStatsManagerInternal usageStats = - LocalServices.getService(UsageStatsManagerInternal.class); - usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(), - mExecutionStartTimeElapsed); - // Once we'e begun executing a job, we by definition no longer care whether // it was inflated from disk with not-yet-coherent delay/deadline bounds. job.clearPersistedUtcTimes(); @@ -267,12 +262,16 @@ public final class JobServiceContext implements ServiceConnection { removeOpTimeOutLocked(); return false; } + mJobPackageTracker.noteActive(job); try { mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid()); } catch (RemoteException e) { // Whatever. } - mJobPackageTracker.noteActive(job); + UsageStatsManagerInternal usageStats = + LocalServices.getService(UsageStatsManagerInternal.class); + usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(), + mExecutionStartTimeElapsed); mAvailable = false; mStoppedReason = null; mStoppedTime = 0; diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 08ff7bdb0eb8..3867306ee521 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -1346,6 +1346,15 @@ public final class JobStatus { } pw.print(prefix); pw.print("Standby bucket: "); pw.println(bucketName(standbyBucket)); + if (standbyBucket > 0) { + pw.print(prefix); pw.print("Base heartbeat: "); + pw.println(baseHeartbeat); + } + if (whenStandbyDeferred != 0) { + pw.print(prefix); pw.print(" Deferred since: "); + TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw); + pw.println(); + } pw.print(prefix); pw.print("Enqueue time: "); TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw); pw.println(); diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS new file mode 100644 index 000000000000..6f8d82306e62 --- /dev/null +++ b/services/core/java/com/android/server/media/OWNERS @@ -0,0 +1,2 @@ +lajos@google.com +elaurent@google.com diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 1746dd1a2b27..9bba9ed0f612 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -289,14 +289,14 @@ public class Installer extends SystemService { int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade, int targetSdkVersion, - @Nullable String profileName, @Nullable String dexMetadataPath) - throws InstallerException { + @Nullable String profileName, @Nullable String dexMetadataPath, + @Nullable String compilationReason) throws InstallerException { assertValidInstructionSet(instructionSet); if (!checkBeforeRemote()) return; try { mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade, - targetSdkVersion, profileName, dexMetadataPath); + targetSdkVersion, profileName, dexMetadataPath, compilationReason); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index fc73142c4858..9420a6c5c15b 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -262,11 +262,12 @@ public class OtaDexoptService extends IOtaDexopt.Stub { int dexFlags, String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade, int targetSdkVersion, @Nullable String profileName, - @Nullable String dexMetadataPath) throws InstallerException { + @Nullable String dexMetadataPath, @Nullable String dexoptCompilationReason) + throws InstallerException { final StringBuilder builder = new StringBuilder(); - // The version. Right now it's 6. - builder.append("6 "); + // The version. Right now it's 7. + builder.append("7 "); builder.append("dexopt"); @@ -285,6 +286,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { encodeParameter(builder, targetSdkVersion); encodeParameter(builder, profileName); encodeParameter(builder, dexMetadataPath); + encodeParameter(builder, dexoptCompilationReason); commands.add(builder.toString()); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 458d725db030..77bf67daa478 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -34,7 +34,6 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.DexoptUtils; import com.android.server.pm.dex.PackageDexUsage; @@ -63,7 +62,8 @@ import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT; -import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter; +import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName; + import static dalvik.system.DexFile.getSafeModeCompilerFilter; import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; @@ -231,7 +231,8 @@ public class PackageDexOptimizer { for (String dexCodeIsa : dexCodeInstructionSets) { int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, - packageStats, options.isDowngrade(), profileName, dexMetadataPath); + packageStats, options.isDowngrade(), profileName, dexMetadataPath, + options.getCompilationReason()); // The end result is: // - FAILED if any path failed, // - PERFORMED if at least one path needed compilation, @@ -256,7 +257,7 @@ public class PackageDexOptimizer { private int dexOptPath(PackageParser.Package pkg, String path, String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, - String profileName, String dexMetadataPath) { + String profileName, String dexMetadataPath, int compilationReason) { int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext, profileUpdated, downgrade); if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { @@ -283,7 +284,7 @@ public class PackageDexOptimizer { mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags, compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo, false /* downgrade*/, pkg.applicationInfo.targetSdkVersion, - profileName, dexMetadataPath); + profileName, dexMetadataPath, getReasonName(compilationReason)); if (packageStats != null) { long endTime = System.currentTimeMillis(); @@ -394,7 +395,7 @@ public class PackageDexOptimizer { // Note this trades correctness for performance since the resulting slow down is // unacceptable in some cases until b/64530081 is fixed. String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK; - + int reason = options.getCompilationReason(); try { for (String isa : dexUseInfo.getLoaderIsas()) { // Reuse the same dexopt path as for the primary apks. We don't need all the @@ -405,7 +406,7 @@ public class PackageDexOptimizer { /*oatDir*/ null, dexoptFlags, compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser, options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null, - /*dexMetadataPath*/ null); + /*dexMetadataPath*/ null, getReasonName(reason)); } return DEX_OPT_PERFORMED; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7ca6bb95c791..884606d23748 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -598,6 +598,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Compilation reasons. + public static final int REASON_UNKNOWN = -1; public static final int REASON_FIRST_BOOT = 0; public static final int REASON_BOOT = 1; public static final int REASON_INSTALL = 2; @@ -8836,7 +8837,7 @@ public class PackageManagerService extends IPackageManager.Stub final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, - getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT), + causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = @@ -8863,7 +8864,7 @@ public class PackageManagerService extends IPackageManager.Stub * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog, - final String compilerFilter, boolean bootComplete) { + final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; @@ -8963,13 +8964,11 @@ public class PackageManagerService extends IPackageManager.Stub } } - String pkgCompilerFilter = compilerFilter; + int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. - pkgCompilerFilter = - PackageManagerServiceCompilerMapping.getCompilerFilterForReason( - PackageManagerService.REASON_BACKGROUND_DEXOPT); + pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } // checkProfiles is false to avoid merging profiles during boot which @@ -8980,7 +8979,7 @@ public class PackageManagerService extends IPackageManager.Stub int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, - pkgCompilerFilter, + pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { @@ -9081,8 +9080,8 @@ public class PackageManagerService extends IPackageManager.Stub int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); - return performDexOpt(new DexoptOptions(packageName, targetCompilerFilter, - splitName, flags)); + return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, + targetCompilerFilter, splitName, flags)); } /** @@ -9191,7 +9190,8 @@ public class PackageManagerService extends IPackageManager.Stub final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), - options.getCompilerFilter(), options.getSplitName(), + options.getCompilationReason(), options.getCompilerFilter(), + options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (PackageParser.Package depPackage : deps) { // TODO: Analyze and investigate if we (should) profile libraries. @@ -20644,10 +20644,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @Override public String getInstallerPackageName(String packageName) { final int callingUid = Binder.getCallingUid(); - if (getInstantAppPackageName(callingUid) != null) { - return null; - } - // reader synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java index 19b0d9bc4b90..fce828581c54 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -123,4 +123,14 @@ public class PackageManagerServiceCompilerMapping { return value; } + + public static String getReasonName(int reason) { + if (reason == PackageManagerService.REASON_UNKNOWN) { + return "unknown"; + } + if (reason < 0 || reason >= REASON_STRINGS.length) { + throw new IllegalArgumentException("reason " + reason + " invalid"); + } + return REASON_STRINGS[reason]; + } } diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 0e2730cbd944..3e63fb42f0ef 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -549,13 +549,12 @@ public class DexManager { mPackageDexUsage.maybeWriteAsync(); } - // Try to optimize the package according to the install reason. - String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason( - PackageManagerService.REASON_INSTALL); DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName) .getDexUseInfoMap().get(dexPath); - DexoptOptions options = new DexoptOptions(info.packageName, compilerFilter, /*flags*/0); + // Try to optimize the package according to the install reason. + DexoptOptions options = new DexoptOptions(info.packageName, + PackageManagerService.REASON_INSTALL, /*flags*/0); int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo, options); diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index d4f95cb6b99f..a7a7686b2a6b 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -77,15 +77,21 @@ public final class DexoptOptions { // It only applies for primary apk and it's always null if mOnlySecondaryDex is true. private final String mSplitName; + // The reason for invoking dexopt (see PackageManagerService.REASON_* constants). + // A -1 value denotes an unknown reason. + private final int mCompilationReason; + public DexoptOptions(String packageName, String compilerFilter, int flags) { - this(packageName, compilerFilter, /*splitName*/ null, flags); + this(packageName, /*compilationReason*/ -1, compilerFilter, /*splitName*/ null, flags); } - public DexoptOptions(String packageName, int compilerReason, int flags) { - this(packageName, getCompilerFilterForReason(compilerReason), flags); + public DexoptOptions(String packageName, int compilationReason, int flags) { + this(packageName, compilationReason, getCompilerFilterForReason(compilationReason), + /*splitName*/ null, flags); } - public DexoptOptions(String packageName, String compilerFilter, String splitName, int flags) { + public DexoptOptions(String packageName, int compilationReason, String compilerFilter, + String splitName, int flags) { int validityMask = DEXOPT_CHECK_FOR_PROFILES_UPDATES | DEXOPT_FORCE | @@ -104,6 +110,7 @@ public final class DexoptOptions { mCompilerFilter = compilerFilter; mFlags = flags; mSplitName = splitName; + mCompilationReason = compilationReason; } public String getPackageName() { @@ -157,4 +164,8 @@ public final class DexoptOptions { public int getFlags() { return mFlags; } + + public int getCompilationReason() { + return mCompilationReason; + } } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index 941cd4441e23..efcadadce3f9 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -19,6 +19,8 @@ package com.android.server.policy.keyguard; import android.app.ActivityManager; import android.content.Context; import android.os.RemoteException; +import android.os.ServiceManager; +import android.security.IKeystoreService; import android.util.Slog; import com.android.internal.policy.IKeyguardService; @@ -51,11 +53,16 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { private final LockPatternUtils mLockPatternUtils; private final StateCallback mCallback; + IKeystoreService mKeystoreService; + public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) { mLockPatternUtils = new LockPatternUtils(context); mCurrentUserId = ActivityManager.getCurrentUser(); mCallback = callback; + mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager + .getService("android.security.keystore")); + try { service.addStateMonitorCallback(this); } catch (RemoteException e) { @@ -86,6 +93,12 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { @Override // Binder interface public void onShowingStateChanged(boolean showing) { mIsShowing = showing; + + if (showing) try { + mKeystoreService.lock(mCurrentUserId); // as long as this doesn't recur... + } catch (RemoteException e) { + Slog.e(TAG, "Error locking keystore", e); + } } @Override // Binder interface diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 9f9b1af5397e..fa7e53594781 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -172,7 +172,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey, long subscriptionId, long subscriptionRuleId, StatsDimensionsValue dimensionsValue) { - if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast."); enforceCallingPermission(); IntentSender intentSender = new IntentSender(intentSenderBinder); Intent intent = new Intent() @@ -181,16 +180,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId) .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId) .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue); + if (DEBUG) { + Slog.d(TAG, String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s}", + configUid, configKey, subscriptionId, + subscriptionRuleId, dimensionsValue)); + } try { intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null); } catch (IntentSender.SendIntentException e) { Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid + "; presumably it had been cancelled."); - if (DEBUG) { - Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}", - configUid, configKey, subscriptionId, - subscriptionRuleId, dimensionsValue)); - } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 75bb5e4f8a6b..61c8b79ae3ce 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -196,7 +196,7 @@ public final class SystemServer { private static final String THERMAL_OBSERVER_CLASS = "com.google.android.clockwork.ThermalObserver"; private static final String WEAR_CONNECTIVITY_SERVICE_CLASS = - "com.google.android.clockwork.connectivity.WearConnectivityService"; + "com.android.clockwork.connectivity.WearConnectivityService"; private static final String WEAR_SIDEKICK_SERVICE_CLASS = "com.google.android.clockwork.sidekick.SidekickService"; private static final String WEAR_DISPLAY_SERVICE_CLASS = diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java index f559986a6f15..93064bc4ab92 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java @@ -118,7 +118,7 @@ public class DexoptOptionsTests { public void testCreateDexoptOptionsSplit() { int flags = DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE; - DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, mSplitName, flags); + DexoptOptions opt = new DexoptOptions(mPackageName, -1, mCompilerFilter, mSplitName, flags); assertEquals(mPackageName, opt.getPackageName()); assertEquals(mCompilerFilter, opt.getCompilerFilter()); assertEquals(mSplitName, opt.getSplitName()); diff --git a/services/usb/OWNERS b/services/usb/OWNERS new file mode 100644 index 000000000000..7897a0c8555c --- /dev/null +++ b/services/usb/OWNERS @@ -0,0 +1,4 @@ +badhri@google.com +elaurent@google.com +moltmann@google.com +zhangjerry@google.com diff --git a/telecomm/OWNERS b/telecomm/OWNERS new file mode 100644 index 000000000000..a3bcfb2cbcfa --- /dev/null +++ b/telecomm/OWNERS @@ -0,0 +1,6 @@ +set noparent + +tgunn@google.com +breadley@google.com +hallliu@google.com +rgreenwalt@google.com diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 8c18518a6d67..0c92c2000f92 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -425,8 +425,14 @@ public final class Call { */ public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200; + /** + * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the + * {@link RttCall} object that is used to send and receive text. + */ + public static final int PROPERTY_RTT = 0x00000400; + //****************************************************************************************** - // Next PROPERTY value: 0x00000400 + // Next PROPERTY value: 0x00000800 //****************************************************************************************** private final String mTelecomCallId; @@ -1189,6 +1195,23 @@ public final class Call { return null; } } + + /** + * Closes the underlying file descriptors + * @hide + */ + public void close() { + try { + mReceiveStream.close(); + } catch (IOException e) { + // ignore + } + try { + mTransmitStream.close(); + } catch (IOException e) { + // ignore + } + } } /** @@ -1664,7 +1687,7 @@ public final class Call { * @return true if there is a connection, false otherwise. */ public boolean isRttActive() { - return mRttCall != null; + return mRttCall != null && mDetails.hasProperty(Details.PROPERTY_RTT); } /** @@ -1867,7 +1890,8 @@ public final class Call { boolean isRttChanged = false; boolean rttModeChanged = false; - if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) { + if (parcelableCall.getIsRttCallChanged() + && mDetails.hasProperty(Details.PROPERTY_RTT)) { ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall(); InputStreamReader receiveStream = new InputStreamReader( new ParcelFileDescriptor.AutoCloseInputStream( diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 322970544281..26a2f1cb8c4f 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -41,6 +41,8 @@ import android.os.SystemClock; import android.util.ArraySet; import android.view.Surface; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; @@ -860,18 +862,19 @@ public abstract class Connection extends Conferenceable { mFdFromInCall = fromInCall; mFdToInCall = toInCall; mPipeFromInCall = new InputStreamReader( - new ParcelFileDescriptor.AutoCloseInputStream(fromInCall)); + new FileInputStream(fromInCall.getFileDescriptor())); mPipeToInCall = new OutputStreamWriter( - new ParcelFileDescriptor.AutoCloseOutputStream(toInCall)); + new FileOutputStream(toInCall.getFileDescriptor())); } /** * Writes the string {@param input} into the text stream to the UI for this RTT call. Since * RTT transmits text in real-time, this method should be called as often as text snippets * are received from the remote user, even if it is only one character. - * + * <p> * This method is not thread-safe -- calling it from multiple threads simultaneously may * lead to interleaved text. + * * @param input The message to send to the in-call app. */ public void write(String input) throws IOException { @@ -884,9 +887,10 @@ public abstract class Connection extends Conferenceable { * Reads a string from the in-call app, blocking if there is no data available. Returns * {@code null} if the RTT conversation has been terminated and there is no further data * to read. - * + * <p> * This method is not thread-safe -- calling it from multiple threads simultaneously may * lead to interleaved text. + * * @return A string containing text entered by the user, or {@code null} if the * conversation has been terminated or if there was an error while reading. */ @@ -901,6 +905,7 @@ public abstract class Connection extends Conferenceable { /** * Non-blocking version of {@link #read()}. Returns {@code null} if there is nothing to * be read. + * * @return A string containing text entered by the user, or {@code null} if the user has * not entered any new text yet. */ @@ -2635,7 +2640,6 @@ public abstract class Connection extends Conferenceable { * {@link #onStartRtt(RttTextStream)} has succeeded. */ public final void sendRttInitiationSuccess() { - setRttProperty(); mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this)); } @@ -2647,7 +2651,6 @@ public abstract class Connection extends Conferenceable { * exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}. */ public final void sendRttInitiationFailure(int reason) { - unsetRttProperty(); mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason)); } @@ -2656,7 +2659,6 @@ public abstract class Connection extends Conferenceable { * side of the coll. */ public final void sendRttSessionRemotelyTerminated() { - unsetRttProperty(); mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this)); } @@ -2956,22 +2958,6 @@ public abstract class Connection extends Conferenceable { */ public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} - /** - * Internal method to set {@link #PROPERTY_IS_RTT}. - * @hide - */ - void setRttProperty() { - setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT); - } - - /** - * Internal method to un-set {@link #PROPERTY_IS_RTT}. - * @hide - */ - void unsetRttProperty() { - setConnectionProperties(getConnectionProperties() & (~PROPERTY_IS_RTT)); - } - static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 658b4734b0b5..b6e6b0ed8270 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -143,6 +143,8 @@ public final class ConnectionRequest implements Parcelable { private final boolean mShouldShowIncomingCallUi; private final ParcelFileDescriptor mRttPipeToInCall; private final ParcelFileDescriptor mRttPipeFromInCall; + // Cached return value of getRttTextStream -- we don't want to wrap it more than once. + private Connection.RttTextStream mRttTextStream; /** * @param accountHandle The accountHandle which should be used to place the call. @@ -312,7 +314,10 @@ public final class ConnectionRequest implements Parcelable { */ public Connection.RttTextStream getRttTextStream() { if (isRequestingRtt()) { - return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall); + if (mRttTextStream == null) { + mRttTextStream = new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall); + } + return mRttTextStream; } else { return null; } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 1547857f23e3..ffa0c946d006 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -143,6 +143,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_HANDOVER_COMPLETE = "CS.hC"; private static final String SESSION_EXTRAS_CHANGED = "CS.oEC"; private static final String SESSION_START_RTT = "CS.+RTT"; + private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT"; private static final String SESSION_STOP_RTT = "CS.-RTT"; private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR"; private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; @@ -1864,7 +1865,6 @@ public abstract class ConnectionService extends Service { Log.d(this, "stopRtt(%s)", callId); if (mConnectionById.containsKey(callId)) { findConnectionForAction(callId, "stopRtt").onStopRtt(); - findConnectionForAction(callId, "stopRtt").unsetRttProperty(); } else if (mConferenceById.containsKey(callId)) { Log.w(this, "stopRtt called on a conference."); } diff --git a/telephony/java/android/telephony/OWNERS b/telephony/OWNERS index 68dedce89272..6f67bc25f879 100644 --- a/telephony/java/android/telephony/OWNERS +++ b/telephony/OWNERS @@ -1,14 +1,14 @@ set noparent -amitmahajan@google.com +tgunn@google.com breadley@google.com -fionaxu@google.com -jackyu@google.com hallliu@google.com rgreenwalt@google.com -tgunn@google.com -jminjie@google.com mpq@google.com +amitmahajan@google.com +fionaxu@google.com +jackyu@google.com +jminjie@google.com +satk@google.com shuoq@google.com refuhoo@google.com - diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index f009fb145fc2..7e86966e2c1b 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -172,7 +172,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** - * Get the timing advance value for LTE, as a value between 0..63. + * Get the timing advance value for LTE, as a value in range of 0..1282. * Integer.MAX_VALUE is reported when there is no active RRC * connection. Refer to 3GPP 36.213 Sec 4.2.3 * @return the LTE timing advance, if available. diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index cb867abb74d4..ec348dfcc047 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1526,7 +1526,9 @@ public class ServiceState implements Parcelable { */ @SystemApi public List<NetworkRegistrationState> getNetworkRegistrationStates() { - return mNetworkRegistrationStates; + synchronized (mNetworkRegistrationStates) { + return new ArrayList<>(mNetworkRegistrationStates); + } } /** @@ -1539,11 +1541,15 @@ public class ServiceState implements Parcelable { @SystemApi public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) { List<NetworkRegistrationState> list = new ArrayList<>(); - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getTransportType() == transportType) { - list.add(networkRegistrationState); + + synchronized (mNetworkRegistrationStates) { + for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { + if (networkRegistrationState.getTransportType() == transportType) { + list.add(networkRegistrationState); + } } } + return list; } @@ -1557,12 +1563,36 @@ public class ServiceState implements Parcelable { */ @SystemApi public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) { - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getTransportType() == transportType - && networkRegistrationState.getDomain() == domain) { - return networkRegistrationState; + synchronized (mNetworkRegistrationStates) { + for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { + if (networkRegistrationState.getTransportType() == transportType + && networkRegistrationState.getDomain() == domain) { + return networkRegistrationState; + } } } + return null; } + + /** + * @hide + */ + public void addNetworkRegistrationState(NetworkRegistrationState regState) { + if (regState == null) return; + + synchronized (mNetworkRegistrationStates) { + for (int i = 0; i < mNetworkRegistrationStates.size(); i++) { + NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i); + if (curRegState.getTransportType() == regState.getTransportType() + && curRegState.getDomain() == regState.getDomain()) { + mNetworkRegistrationStates.remove(i); + break; + } + } + + mNetworkRegistrationStates.add(regState); + } + } + } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7afd28ce181f..fefc03d785c4 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -53,6 +53,7 @@ import android.telephony.ims.aidl.IImsMmTelFeature; import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; import com.android.ims.internal.IImsServiceFeatureCallback; @@ -6410,84 +6411,106 @@ public class TelephonyManager { return false; } - /** - * Returns the IMS Registration Status - * @hide - */ - public boolean isImsRegistered() { - try { - ITelephony telephony = getITelephony(); - if (telephony == null) - return false; - return telephony.isImsRegistered(); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { - return false; - } - } - /** - * Returns the IMS Registration Status for a particular Subscription ID + * Returns the IMS Registration Status for a particular Subscription ID. * * @param subId Subscription ID * @return true if IMS status is registered, false if the IMS status is not registered or a * RemoteException occurred. - * * @hide */ public boolean isImsRegistered(int subId) { + try { + return getITelephony().isImsRegistered(subId); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * Returns the IMS Registration Status for a particular Subscription ID, which is determined + * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an + * invalid subscription ID is used during creation, will the default subscription ID will be + * used. + * + * @return true if IMS status is registered, false if the IMS status is not registered or a + * RemoteException occurred. + * @see SubscriptionManager#getDefaultSubscriptionId() + * @hide + */ + public boolean isImsRegistered() { try { - return getITelephony().isImsRegisteredForSubscriber(subId); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { + return getITelephony().isImsRegistered(getSubId()); + } catch (RemoteException | NullPointerException ex) { return false; } } /** - * Returns the Status of Volte + * The current status of Voice over LTE for the subscription associated with this instance when + * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was + * used during creation, the default subscription ID will be used. + * @return true if Voice over LTE is available or false if it is unavailable or unknown. + * @see SubscriptionManager#getDefaultSubscriptionId() * @hide */ public boolean isVolteAvailable() { - try { - return getITelephony().isVolteAvailable(); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { - return false; - } - } + try { + return getITelephony().isVolteAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } /** - * Returns the Status of video telephony (VT) + * The availability of Video Telephony (VT) for the subscription ID specified when this instance + * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was + * used during creation, the default subscription ID will be used. To query the + * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}. + * @return true if VT is available, or false if it is unavailable or unknown. * @hide */ public boolean isVideoTelephonyAvailable() { try { - return getITelephony().isVideoTelephonyAvailable(); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { + return getITelephony().isVideoTelephonyAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { return false; } } /** - * Returns the Status of Wi-Fi Calling + * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified. + * @param subId the subscription ID. + * @return true if VoWiFi is available, or false if it is unavailable or unknown. * @hide */ public boolean isWifiCallingAvailable() { try { - return getITelephony().isWifiCallingAvailable(); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { + return getITelephony().isWifiCallingAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { return false; } } + /** + * The technology that IMS is registered for for the MMTEL feature. + * @param subId subscription ID to get IMS registration technology for. + * @return The IMS registration technology that IMS is registered to for the MMTEL feature. + * Valid return results are: + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration, + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the + * result is unavailable. + * @hide + */ + public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() { + try { + return getITelephony().getImsRegTechnologyForMmTel(getSubId()); + } catch (RemoteException ex) { + return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; + } + } + /** * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone. * diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index bfdd4533275b..1fdbae9186b7 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -80,7 +80,7 @@ public abstract class ImsFeature { public static final String EXTRA_PHONE_ID = "android:phone_id"; /** - * Invalid feature value\ + * Invalid feature value * @hide */ public static final int FEATURE_INVALID = -1; diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 2b4c059cf69f..02cc82cf56b0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1123,33 +1123,33 @@ interface ITelephony { boolean isHearingAidCompatibilitySupported(); /** - * Get IMS Registration Status - */ - boolean isImsRegistered(); - - /** * Get IMS Registration Status on a particular subid. * * @param subId user preferred subId. * * @return {@code true} if the IMS status is registered. */ - boolean isImsRegisteredForSubscriber(int subId); + boolean isImsRegistered(int subId); /** - * Returns the Status of Wi-Fi Calling + * Returns the Status of Wi-Fi Calling for the subscription id specified. */ - boolean isWifiCallingAvailable(); + boolean isWifiCallingAvailable(int subId); /** - * Returns the Status of Volte + * Returns the Status of VoLTE for the subscription ID specified. */ - boolean isVolteAvailable(); + boolean isVolteAvailable(int subId); /** - * Returns the Status of VT (video telephony) + * Returns the Status of VT (video telephony) for the subscription ID specified. */ - boolean isVideoTelephonyAvailable(); + boolean isVideoTelephonyAvailable(int subId); + + /** + * Returns the MMTEL IMS registration technology for the subsciption ID specified. + */ + int getImsRegTechnologyForMmTel(int subId); /** * Returns the unique device ID of phone, for example, the IMEI for diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt index 77e0e90623fa..1040ed169c5b 100644 --- a/tests/ActivityManagerPerfTests/README.txt +++ b/tests/ActivityManagerPerfTests/README.txt @@ -1,12 +1,15 @@ ActivityManagerPerfTests Performance tests for various ActivityManager components, e.g. Services, Broadcasts +* These are only for tests that don't require a target package to test against +* Self-contained perf tests should go in frameworks/base/apct-tests/perftests -Command to run tests (not working yet, atest seems buggy) -* atest .../frameworks/base/tests/ActivityManagerPerfTests +Command to run tests +* atest .../frameworks/base/tests/ActivityManagerPerfTests/tests/ + * Command currently not working: b/71859981 * m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \ - adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \ - adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \ + adb install "$OUT"/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \ + adb install "$OUT"/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \ adb shell am instrument -w \ com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner @@ -15,20 +18,42 @@ Overview * For example, the time it takes from sending an Intent to start a Service to the time the Service runs its callbacks * System.nanoTime() is monotonic and consistent between processes, so we use that for measuring time -* To make sure the test app is running, we start an Activity * If the test app is involved, it will measure the time and send it back to the instrumentation test - * The time is sent back through a Binder interface in the Intent + * The time is sent back through a Binder interface in the Intent with the help of Utils.sendTime() * Each sent time is tagged with an id since there can be multiple events that send back a time - * For example, one is sent when the Activity is started, and another could be sent when a - Broadcast is received +* Each test will run multiple times to account for variation in test runs Structure * tests * Instrumentation test which runs the various performance tests and reports the results - * test-app * Target package which contains the Services, BroadcastReceivers, etc. to test against * Sends the time it measures back to the test package - * utils * Utilities that both the instrumentation test and test app can use + +Adding tests +* Example + * Look at tests/src/com/android/frameworks/perftests/am/BroadcastPerfTest and + test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver + for simple examples using this framework +* Steps + * Add any components you will test against in the target package under + test-app/src/com/android/frameworks/perftests/amteststestapp/ + * Add the test class under tests/src/com/android/frameworks/perftests/am/tests/ + * The class should extend BasePerfTest + * Each test should call runPerfFunction() returning the elapsed time for a single iteration + * The test has access to a Context through mContext + * If you are measuring the time elapsed of something that either starts or ends in the target + package + * The target package can report the time it measures through an ITimeReceiverCallback passed + through an Intent through Utils.sendTime(intent, "tag") + (or however a Binder needs to be passed to the target package) + * The instrumentation test can collect that time by calling getReceivedTimeNs("tag") and + calculate the elapsed time + * Each timestamp sent to the instrumentation test is tagged with a tag since multiple timestamps + can be reported in an iteration + * If the target package should be running before your test logic starts, add startTargetPackage(); + at the beginning of the iteration +* Reporting + * Look at go/am-perf for how to add new tests to dashboards and receive notification on regression diff --git a/tests/ActivityManagerPerfTests/test-app/Android.mk b/tests/ActivityManagerPerfTests/test-app/Android.mk index b0a5db7a3134..767e899450cf 100644 --- a/tests/ActivityManagerPerfTests/test-app/Android.mk +++ b/tests/ActivityManagerPerfTests/test-app/Android.mk @@ -23,6 +23,8 @@ LOCAL_SRC_FILES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ ActivityManagerPerfTestsUtils +LOCAL_MIN_SDK_VERSION := 25 + LOCAL_PACKAGE_NAME := ActivityManagerPerfTestsTestApp include $(BUILD_PACKAGE) diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk index daf603d6a63f..7597e69a4006 100644 --- a/tests/ActivityManagerPerfTests/tests/Android.mk +++ b/tests/ActivityManagerPerfTests/tests/Android.mk @@ -27,6 +27,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_PACKAGE_NAME := ActivityManagerPerfTests +LOCAL_MIN_SDK_VERSION := 25 + # For android.permission.FORCE_STOP_PACKAGES permission LOCAL_CERTIFICATE := platform diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 063060f166dc..83354d55b005 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -65,6 +65,7 @@ public class AppLaunch extends InstrumentationTestCase { private static final int JOIN_TIMEOUT = 10000; private static final String TAG = AppLaunch.class.getSimpleName(); + // optional parameter: comma separated list of required account types before proceeding // with the app launch private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts"; @@ -73,32 +74,36 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations"; private static final String KEY_LAUNCH_ORDER = "launch_order"; private static final String KEY_DROP_CACHE = "drop_cache"; - private static final String KEY_SIMULATE_MAINTANANCE = "simulate_maintanance"; - private static final String KEY_SIMPLEPPERF_CMD = "simpleperf_cmd"; + private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd"; + private static final String KEY_SIMPLEPERF_APP = "simpleperf_app"; private static final String KEY_TRACE_ITERATIONS = "trace_iterations"; private static final String KEY_LAUNCH_DIRECTORY = "launch_directory"; private static final String KEY_TRACE_DIRECTORY = "trace_directory"; private static final String KEY_TRACE_CATEGORY = "trace_categories"; private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize"; private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval"; + private static final String KEY_COMPILER_FILTERS = "compiler_filters"; + + private static final String SIMPLEPERF_APP_CMD = + "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s"; private static final String WEARABLE_ACTION_GOOGLE = "com.google.android.wearable.action.GOOGLE"; - private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle - private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches - private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps + private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle + private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches + private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps private static final String LAUNCH_SUB_DIRECTORY = "launch_logs"; private static final String LAUNCH_FILE = "applaunch.txt"; private static final String TRACE_SUB_DIRECTORY = "atrace_logs"; - private static final String DEFAULT_TRACE_CATEGORIES = "sched,freq,gfx,view,dalvik,webview," - + "input,wm,disk,am,wm"; + private static final String DEFAULT_TRACE_CATEGORIES = + "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm"; private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000"; private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10"; - private static final String TRIAL_LAUNCH = "TRAIL_LAUNCH"; + private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH"; private static final String DELIMITER = ","; private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh"; private static final String APP_LAUNCH_CMD = "am start -W -n"; private static final String SUCCESS_MESSAGE = "Status: ok"; - private static final String PROFILE_COMPILE_SUCCESS = "Success"; + private static final String COMPILE_SUCCESS = "Success"; private static final String THIS_TIME = "ThisTime:"; private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d"; private static final String TRACE_ITERATION = "TRACE_ITERATION-%d"; @@ -106,14 +111,15 @@ public class AppLaunch extends InstrumentationTestCase { private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION"; private static final String LAUNCH_ORDER_CYCLIC = "cyclic"; private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential"; - private static final String SPEED_PROFILE_CMD = "cmd package compile -f -m speed-profile %s"; - - + private static final String COMPILE_CMD = "cmd package compile -f -m %s %s"; + private static final String SPEED_PROFILE_FILTER = "speed-profile"; + private static final String VERIFY_FILTER = "verify"; + private static final String LAUNCH_SCRIPT_NAME = "appLaunch"; private Map<String, Intent> mNameToIntent; private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>(); private Map<String, String> mNameToResultKey; - private Map<String, List<Long>> mNameToLaunchTime; + private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime; private IActivityManager mAm; private String mSimplePerfCmd = null; private String mLaunchOrder = null; @@ -123,12 +129,10 @@ public class AppLaunch extends InstrumentationTestCase { private String mTraceDirectoryStr = null; private Bundle mResult = new Bundle(); private Set<String> mRequiredAccounts; - private boolean mTrailLaunch = true; - private File mFile = null; - private FileOutputStream mOutputStream = null; + private boolean mTrialLaunch = false; private BufferedWriter mBufferedWriter = null; - private boolean mSimulateMaintanance = false; - + private boolean mSimplePerfAppOnly = false; + private String[] mCompilerFilters = null; @Override protected void setUp() throws Exception { @@ -142,6 +146,16 @@ public class AppLaunch extends InstrumentationTestCase { super.tearDown(); } + private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) { + mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result); + } + + private boolean hasFailureOnFirstLaunch(LaunchOrder launch) { + List<AppLaunchResult> results = + mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()); + return (results.size() > 0) && (results.get(0).mLaunchTime < 0); + } + public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException, IOException, InterruptedException { InstrumentationTestRunner instrumentation = @@ -149,11 +163,6 @@ public class AppLaunch extends InstrumentationTestCase { Bundle args = instrumentation.getArguments(); mAm = ActivityManager.getService(); String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY); - mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY); - mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE)); - mSimplePerfCmd = args.getString(KEY_SIMPLEPPERF_CMD); - mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC); - mSimulateMaintanance = Boolean.parseBoolean(args.getString(KEY_SIMULATE_MAINTANANCE)); createMappings(); parseArgs(args); @@ -171,13 +180,14 @@ public class AppLaunch extends InstrumentationTestCase { try { File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY); + if (!launchSubDir.exists() && !launchSubDir.mkdirs()) { throw new IOException("Unable to create the lauch file sub directory"); } - mFile = new File(launchSubDir, LAUNCH_FILE); - mOutputStream = new FileOutputStream(mFile); + File file = new File(launchSubDir, LAUNCH_FILE); + FileOutputStream outputStream = new FileOutputStream(file); mBufferedWriter = new BufferedWriter(new OutputStreamWriter( - mOutputStream)); + outputStream)); // Root directory for trace file during the launches File rootTrace = null; @@ -217,70 +227,67 @@ public class AppLaunch extends InstrumentationTestCase { setLaunchOrder(); for (LaunchOrder launch : mLaunchOrderList) { + dropCache(); // App launch times for trial launch will not be used for final // launch time calculations. if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) { // In the "applaunch.txt" file, trail launches is referenced using // "TRIAL_LAUNCH" - long launchTime = startApp(launch.getApp(), true, launch.getLaunchReason()); - if (launchTime < 0) { - List<Long> appLaunchList = new ArrayList<Long>(); - appLaunchList.add(-1L); - mNameToLaunchTime.put(launch.getApp(), appLaunchList); + String appPkgName = mNameToIntent.get(launch.getApp()) + .getComponent().getPackageName(); + if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) { + assertTrue(String.format("Not able to compile the app : %s", appPkgName), + compileApp(VERIFY_FILTER, appPkgName)); + } else if (launch.getCompilerFilter() != null) { + assertTrue(String.format("Not able to compile the app : %s", appPkgName), + compileApp(launch.getCompilerFilter(), appPkgName)); + } + // We only need to run a trial for the speed-profile filter, but we always + // run one for "applaunch.txt" consistency. + AppLaunchResult launchResult = + startApp(launch.getApp(), true, launch.getLaunchReason()); + if (launchResult.mLaunchTime < 0) { + addLaunchResult(launch, new AppLaunchResult()); // simply pass the app if launch isn't successful // error should have already been logged by startApp continue; } sleep(INITIAL_LAUNCH_IDLE_TIMEOUT); - closeApp(launch.getApp(), true); - dropCache(); - if (mSimulateMaintanance) { - String appPkgName = mNameToIntent.get(launch.getApp()) - .getComponent().getPackageName(); - assertTrue(String.format("Not able to speed profile the app : %s", - appPkgName), profileCompileApp(appPkgName)); + if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) { + // Send SIGUSR1 to force dumping a profile. + String sendSignalCommand = + String.format("killall -s SIGUSR1 %s", appPkgName); + getInstrumentation().getUiAutomation().executeShellCommand( + sendSignalCommand); + assertTrue(String.format("Not able to compile the app : %s", appPkgName), + compileApp(launch.getCompilerFilter(), appPkgName)); } - sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); } // App launch times used for final calculation - if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) { - long launchTime = -1; - if (null != mNameToLaunchTime.get(launch.getApp())) { - long firstLaunchTime = mNameToLaunchTime.get(launch.getApp()).get(0); - if (firstLaunchTime < 0) { - // skip if the app has failures while launched first - continue; - } + else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) { + AppLaunchResult launchResults = null; + if (hasFailureOnFirstLaunch(launch)) { + // skip if the app has failures while launched first + continue; } // In the "applaunch.txt" file app launches are referenced using // "LAUNCH_ITERATION - ITERATION NUM" - launchTime = startApp(launch.getApp(), true, launch.getLaunchReason()); - if (launchTime < 0) { + launchResults = startApp(launch.getApp(), true, launch.getLaunchReason()); + if (launchResults.mLaunchTime < 0) { + addLaunchResult(launch, new AppLaunchResult()); // if it fails once, skip the rest of the launches - List<Long> appLaunchList = new ArrayList<Long>(); - appLaunchList.add(-1L); - mNameToLaunchTime.put(launch.getApp(), appLaunchList); continue; } else { - if (null != mNameToLaunchTime.get(launch.getApp())) { - mNameToLaunchTime.get(launch.getApp()).add(launchTime); - } else { - List<Long> appLaunchList = new ArrayList<Long>(); - appLaunchList.add(launchTime); - mNameToLaunchTime.put(launch.getApp(), appLaunchList); - } + addLaunchResult(launch, launchResults); } sleep(POST_LAUNCH_IDLE_TIMEOUT); - closeApp(launch.getApp(), true); - dropCache(); - sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); } // App launch times for trace launch will not be used for final // launch time calculations. - if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) { + else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) { AtraceLogger atraceLogger = AtraceLogger .getAtraceLoggerInstance(getInstrumentation()); // Start the trace @@ -293,11 +300,10 @@ public class AppLaunch extends InstrumentationTestCase { } finally { // Stop the trace atraceLogger.atraceStop(); - closeApp(launch.getApp(), true); - dropCache(); - sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); } } + closeApp(launch.getApp()); + sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); } } finally { if (null != mBufferedWriter) { @@ -306,29 +312,45 @@ public class AppLaunch extends InstrumentationTestCase { } for (String app : mNameToResultKey.keySet()) { - StringBuilder launchTimes = new StringBuilder(); - for (Long launch : mNameToLaunchTime.get(app)) { - launchTimes.append(launch); - launchTimes.append(","); + for (String compilerFilter : mCompilerFilters) { + StringBuilder launchTimes = new StringBuilder(); + StringBuilder cpuCycles = new StringBuilder(); + StringBuilder majorFaults = new StringBuilder(); + for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) { + launchTimes.append(result.mLaunchTime); + launchTimes.append(","); + if (mSimplePerfAppOnly) { + cpuCycles.append(result.mCpuCycles); + cpuCycles.append(","); + majorFaults.append(result.mMajorFaults); + majorFaults.append(","); + } + } + String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter); + mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString()); + if (mSimplePerfAppOnly) { + mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles", + cpuCycles.toString()); + mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults", + majorFaults.toString()); + } } - mResult.putString(mNameToResultKey.get(app), launchTimes.toString()); } instrumentation.sendStatus(0, mResult); } /** - * Compile the app package using speed compile command and return true or false + * Compile the app package using compilerFilter and return true or false * based on status of the compilation command. */ - private boolean profileCompileApp(String appPkgName) throws IOException { - Log.i(TAG, "Starting to speed profile " + appPkgName); + private boolean compileApp(String compilerFilter, String appPkgName) throws IOException { try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation(). - executeShellCommand(String.format(SPEED_PROFILE_CMD, appPkgName)); + executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName)); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( new FileInputStream(result.getFileDescriptor())))) { String line; while ((line = bufferedReader.readLine()) != null) { - if (line.contains(PROFILE_COMPILE_SUCCESS)) { + if (line.contains(COMPILE_SUCCESS)) { return true; } } @@ -344,38 +366,42 @@ public class AppLaunch extends InstrumentationTestCase { */ private void setLaunchOrder() { if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) { - if (mTrailLaunch) { - for (String app : mNameToResultKey.keySet()) { - mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH)); - } - } - for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { - for (String app : mNameToResultKey.keySet()) { - mLaunchOrderList.add(new LaunchOrder(app, - String.format(LAUNCH_ITERATION, launchCount))); - } - } - if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { - for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { + for (String compilerFilter : mCompilerFilters) { + if (mTrialLaunch) { for (String app : mNameToResultKey.keySet()) { - mLaunchOrderList.add(new LaunchOrder(app, - String.format(TRACE_ITERATION, traceCount))); + mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH)); } } - } - } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) { - for (String app : mNameToResultKey.keySet()) { - if (mTrailLaunch) { - mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH)); - } for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { - mLaunchOrderList.add(new LaunchOrder(app, - String.format(LAUNCH_ITERATION, launchCount))); + for (String app : mNameToResultKey.keySet()) { + mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, + String.format(LAUNCH_ITERATION, launchCount))); + } } if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { - mLaunchOrderList.add(new LaunchOrder(app, - String.format(TRACE_ITERATION, traceCount))); + for (String app : mNameToResultKey.keySet()) { + mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, + String.format(TRACE_ITERATION, traceCount))); + } + } + } + } + } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) { + for (String compilerFilter : mCompilerFilters) { + for (String app : mNameToResultKey.keySet()) { + if (mTrialLaunch) { + mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH)); + } + for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { + mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, + String.format(LAUNCH_ITERATION, launchCount))); + } + if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { + for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { + mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, + String.format(TRACE_ITERATION, traceCount))); + } } } } @@ -385,7 +411,7 @@ public class AppLaunch extends InstrumentationTestCase { } private void dropCache() { - if (true == mDropCache) { + if (mDropCache) { assertNotNull("Issue in dropping the cache", getInstrumentation().getUiAutomation() .executeShellCommand(DROP_CACHE_SCRIPT)); @@ -394,7 +420,7 @@ public class AppLaunch extends InstrumentationTestCase { private void parseArgs(Bundle args) { mNameToResultKey = new LinkedHashMap<String, String>(); - mNameToLaunchTime = new HashMap<String, List<Long>>(); + mNameToLaunchTime = new HashMap<>(); String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS); if (launchIterations != null) { mLaunchIterations = Integer.parseInt(launchIterations); @@ -421,7 +447,38 @@ public class AppLaunch extends InstrumentationTestCase { mRequiredAccounts.add(accountType); } } - mTrailLaunch = "true".equals(args.getString(KEY_TRIAL_LAUNCH)); + + String compilerFilterList = args.getString(KEY_COMPILER_FILTERS); + if (compilerFilterList != null) { + // If a compiler filter is passed, we make a trial launch to force compilation + // of the apps. + mTrialLaunch = true; + mCompilerFilters = compilerFilterList.split("\\|"); + } else { + // Just pass a null compiler filter to use the current state of the app. + mCompilerFilters = new String[1]; + } + + // Pre-populate the results map to avoid null checks. + for (String app : mNameToLaunchTime.keySet()) { + HashMap<String, List<AppLaunchResult>> map = new HashMap<>(); + mNameToLaunchTime.put(app, map); + for (String compilerFilter : mCompilerFilters) { + map.put(compilerFilter, new ArrayList<>()); + } + } + + mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY); + mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE)); + mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD); + mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC); + mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP)); + mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH)); + + if (mSimplePerfCmd != null && mSimplePerfAppOnly) { + Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s", + KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP)); + } } private boolean hasLeanback(Context context) { @@ -465,17 +522,17 @@ public class AppLaunch extends InstrumentationTestCase { } } - private long startApp(String appName, boolean forceStopBeforeLaunch, String launchReason) - throws NameNotFoundException, RemoteException { + private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch, + String launchReason) throws NameNotFoundException, RemoteException { Log.i(TAG, "Starting " + appName); Intent startIntent = mNameToIntent.get(appName); if (startIntent == null) { Log.w(TAG, "App does not exist: " + appName); mResult.putString(mNameToResultKey.get(appName), "App does not exist"); - return -1L; + return new AppLaunchResult(); } - AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch , + AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch, launchReason); Thread t = new Thread(runnable); t.start(); @@ -518,22 +575,23 @@ public class AppLaunch extends InstrumentationTestCase { } } - private void closeApp(String appName, boolean forceStopApp) { + private void startHomeIntent() { Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); getInstrumentation().getContext().startActivity(homeIntent); sleep(POST_LAUNCH_IDLE_TIMEOUT); - if (forceStopApp) { - Intent startIntent = mNameToIntent.get(appName); - if (startIntent != null) { - String packageName = startIntent.getComponent().getPackageName(); - try { - mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); - } catch (RemoteException e) { - Log.w(TAG, "Error closing app", e); - } + } + + private void closeApp(String appName) { + Intent startIntent = mNameToIntent.get(appName); + if (startIntent != null) { + String packageName = startIntent.getComponent().getPackageName(); + try { + mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); + } catch (RemoteException e) { + Log.w(TAG, "Error closing app", e); } } } @@ -569,10 +627,12 @@ public class AppLaunch extends InstrumentationTestCase { private class LaunchOrder { private String mApp; + private String mCompilerFilter; private String mLaunchReason; - LaunchOrder(String app,String launchReason){ + LaunchOrder(String app, String compilerFilter, String launchReason){ mApp = app; + mCompilerFilter = compilerFilter; mLaunchReason = launchReason; } @@ -584,6 +644,10 @@ public class AppLaunch extends InstrumentationTestCase { mApp = app; } + public String getCompilerFilter() { + return mCompilerFilter; + } + public String getLaunchReason() { return mLaunchReason; } @@ -593,9 +657,31 @@ public class AppLaunch extends InstrumentationTestCase { } } + private class AppLaunchResult { + long mLaunchTime; + long mCpuCycles; + long mMajorFaults; + + AppLaunchResult() { + mLaunchTime = -1L; + mCpuCycles = -1L; + mMajorFaults = -1L; + } + + AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) { + try { + mLaunchTime = Long.parseLong(launchTime, 10); + mCpuCycles = Long.parseLong(cpuCycles, 10); + mMajorFaults = Long.parseLong(majorFaults, 10); + } catch (NumberFormatException e) { + Log.e(TAG, "Error parsing result", e); + } + } + } + private class AppLaunchRunnable implements Runnable { private Intent mLaunchIntent; - private Long mResult; + private AppLaunchResult mLaunchResult; private boolean mForceStopBeforeLaunch; private String mLaunchReason; @@ -604,14 +690,15 @@ public class AppLaunch extends InstrumentationTestCase { mLaunchIntent = intent; mForceStopBeforeLaunch = forceStopBeforeLaunch; mLaunchReason = launchReason; - mResult = -1L; + mLaunchResult = new AppLaunchResult(); } - public Long getResult() { - return mResult; + public AppLaunchResult getResult() { + return mLaunchResult; } public void run() { + File launchFile = null; try { String packageName = mLaunchIntent.getComponent().getPackageName(); String componentName = mLaunchIntent.getComponent().flattenToShortString(); @@ -619,17 +706,38 @@ public class AppLaunch extends InstrumentationTestCase { mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); } String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName); - if (null != mSimplePerfCmd) { + if (mSimplePerfAppOnly) { + try { + // executeShellCommand cannot handle shell specific actions, like '&'. + // Therefore, we create a file containing the command and make that + // the command to launch. + launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh"); + launchFile.setExecutable(true); + try (FileOutputStream stream = new FileOutputStream(launchFile); + BufferedWriter writer = + new BufferedWriter(new OutputStreamWriter(stream))) { + String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd); + writer.write(cmd); + } + launchCmd = launchFile.getAbsolutePath(); + } catch (IOException e) { + Log.w(TAG, "Error writing the launch command", e); + return; + } + } else if (null != mSimplePerfCmd) { launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd); } Log.v(TAG, "Final launch cmd:" + launchCmd); ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation() .executeShellCommand(launchCmd); - mResult = Long.parseLong(parseLaunchTimeAndWrite(parcelDesc, String.format - ("App Launch :%s %s", - componentName, mLaunchReason)), 10); + mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format + ("App Launch :%s %s", componentName, mLaunchReason)); } catch (RemoteException e) { Log.w(TAG, "Error launching app", e); + } finally { + if (launchFile != null) { + launchFile.delete(); + } } } @@ -639,12 +747,14 @@ public class AppLaunch extends InstrumentationTestCase { * @param parcelDesc * @return */ - private String parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc, String headerInfo) { + private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc, + String headerInfo) { String launchTime = "-1"; + String cpuCycles = "-1"; + String majorFaults = "-1"; boolean launchSuccess = false; try { InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor()); - StringBuilder appLaunchOuput = new StringBuilder(); /* SAMPLE OUTPUT : Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator } Status: ok @@ -653,6 +763,11 @@ public class AppLaunch extends InstrumentationTestCase { TotalTime: 357 WaitTime: 377 Complete*/ + /* WITH SIMPLEPERF : + Performance counter statistics, + 6595722690,cpu-cycles,4.511040,GHz,(100%), + 0,major-faults,0.000,/sec,(100%), + Total test time,1.462129,seconds,*/ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( inputStream)); String line = null; @@ -669,6 +784,23 @@ public class AppLaunch extends InstrumentationTestCase { String launchSplit[] = line.split(":"); launchTime = launchSplit[1].trim(); } + + if (mSimplePerfAppOnly) { + // Parse simpleperf output. + if (lineCount == 9) { + if (!line.contains("cpu-cycles")) { + Log.e(TAG, "Error in simpleperf output"); + } else { + cpuCycles = line.split(",")[0].trim(); + } + } else if (lineCount == 10) { + if (!line.contains("major-faults")) { + Log.e(TAG, "Error in simpleperf output"); + } else { + majorFaults = line.split(",")[0].trim(); + } + } + } mBufferedWriter.write(line); mBufferedWriter.newLine(); lineCount++; @@ -678,7 +810,7 @@ public class AppLaunch extends InstrumentationTestCase { } catch (IOException e) { Log.w(TAG, "Error writing the launch file", e); } - return launchTime; + return new AppLaunchResult(launchTime, cpuCycles, majorFaults); } } diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index bdbc149a0a42..a61ac54e0ff1 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -15,6 +15,7 @@ import android.net.wifi.rtt.WifiRttManager; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -986,11 +987,16 @@ public class RttManager { legacyResults[i] = new RttResult(); legacyResults[i].status = result.getStatus(); legacyResults[i].bssid = result.getMacAddress().toString(); - legacyResults[i].distance = result.getDistanceMm() / 10; - legacyResults[i].distanceStandardDeviation = - result.getDistanceStdDevMm() / 10; - legacyResults[i].rssi = result.getRssi(); - legacyResults[i].ts = result.getRangingTimestampUs(); + if (result.getStatus() == RangingResult.STATUS_SUCCESS) { + legacyResults[i].distance = result.getDistanceMm() / 10; + legacyResults[i].distanceStandardDeviation = + result.getDistanceStdDevMm() / 10; + legacyResults[i].rssi = result.getRssi(); + legacyResults[i].ts = result.getRangingTimestampUs(); + } else { + // just in case legacy API needed some relatively real timestamp + legacyResults[i].ts = SystemClock.elapsedRealtime() * 1000; + } } listener.onSuccess(legacyResults); } |