diff options
101 files changed, 2227 insertions, 1083 deletions
diff --git a/api/current.txt b/api/current.txt index df18f10243e0..63bc2ecaca13 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 } 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/Android.mk b/cmds/incidentd/Android.mk index 2b00d9e6a596..23cd2af512b5 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -56,6 +56,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ 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/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 3f0e331c8b55..c5078f0f7909 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -73,11 +73,6 @@ PrivacySpec PrivacySpec::new_spec(int dest) 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..ce1b8e96529c 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -75,7 +75,6 @@ public: // 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) {} }; diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index b9f479bd683f..06baebaf3a4c 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -18,6 +18,7 @@ #include "Reporter.h" +#include "Privacy.h" #include "report_directory.h" #include "section_list.h" @@ -65,7 +66,9 @@ ReportRequestSet::ReportRequestSet() :mRequests(), mSections(), mMainFd(-1), - mMainDest(-1) + mMainDest(-1), + mMetadata(), + mSectionStats() { } @@ -79,18 +82,32 @@ 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) { mMainFd = fd; + mMetadata.set_use_dropbox(fd > 0); } 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 @@ -98,6 +115,16 @@ 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; }; @@ -128,12 +155,12 @@ 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++) { @@ -182,7 +209,7 @@ Reporter::runReport() 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 + // Notify listener of starting. for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { (*it)->listener->onReportSectionStatus(id, @@ -191,14 +218,20 @@ Reporter::runReport() } // 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)); goto DONE; } - // Notify listener of starting + // Notify listener of ending. for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { (*it)->listener->onReportSectionStatus(id, @@ -210,6 +243,9 @@ Reporter::runReport() } DONE: + // Reports the metdadata when taking the incident report. + if (!isTest) metadataSection.Execute(&batch); + // Close the file. if (mainFd >= 0) { close(mainFd); diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index f30ecf0dd648..6058068be331 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -17,9 +17,12 @@ #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> @@ -61,13 +64,20 @@ 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; }; // ================================================================================ @@ -81,8 +91,8 @@ public: 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. diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index faeab87f8178..3c76298c284c 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -45,6 +45,7 @@ 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; @@ -149,6 +150,12 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re 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; @@ -182,9 +189,7 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re // 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 (privacyBuffer.size() == 0) goto DONE; @@ -196,6 +201,7 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re writeable++; ALOGD("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: @@ -236,7 +242,7 @@ HeaderSection::Execute(ReportRequestSet* requests) const // 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_section_header(fd, id, buf->size()); write_all(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. @@ -244,7 +250,35 @@ HeaderSection::Execute(ReportRequestSet* requests) const } 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()); + write_all(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size()); + } + if (requests->mainFd() >= 0 && !metadataBuf.empty()) { + write_section_header(requests->mainFd(), id, metadataBuf.size()); + write_all(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size()); + } + return NO_ERROR; +} // ================================================================================ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) :Section(id, timeoutMs), diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index d440ee92601c..80cc033d5ca0 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -59,6 +59,18 @@ 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 diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index c494bd646b0b..a1e3c3430595 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -180,3 +180,18 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { ASSERT_EQ((int)results.size(), 1); 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..5752a670c6a2 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -18,6 +18,7 @@ #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> @@ -89,6 +90,18 @@ TEST(SectionTest, HeaderSection) { 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)); @@ -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)); @@ -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/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/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index ddc05c64415a..e75b710cc9db 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -190,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/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/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/provider/Settings.java b/core/java/android/provider/Settings.java index 05ab486aaaeb..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 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/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..790e227071a9 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); } } @@ -8807,6 +8807,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 +8822,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - return false; + return true; } /** diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index cf81b979cff4..95c12fb8c1db 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1089,16 +1089,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(); } } @@ -1119,6 +1119,7 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } + if (sVerbose) Log.v(TAG, "commit() called by app"); synchronized (mLock) { commitLocked(); } @@ -2332,6 +2333,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 @@ -2351,7 +2353,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); } @@ -2457,6 +2459,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/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/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/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/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-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 e994d37e45b8..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> 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 f69104476e49..837113d4845e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2171,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> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 348f0b9a3753..9995642ba455 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2990,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" /> @@ -3018,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/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index ee7abc5bd254..acefead785c4 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -444,6 +444,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 +783,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 +905,6 @@ public final class ImageDecoder implements AutoCloseable { postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, mPreferRamOverQuality, mAsAlphaMask); - } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -965,7 +977,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 +1023,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/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/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/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/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/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 50df021d707b..c047670f95db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -908,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/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/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/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/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 b9f6d211c7f7..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. 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/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/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/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 063060f166dc..228f9bbddcd1 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(), true); + 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(); @@ -569,10 +626,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 +643,10 @@ public class AppLaunch extends InstrumentationTestCase { mApp = app; } + public String getCompilerFilter() { + return mCompilerFilter; + } + public String getLaunchReason() { return mLaunchReason; } @@ -593,9 +656,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 +689,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 +705,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 +746,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 +762,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 +783,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 +809,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); } |