diff options
173 files changed, 4592 insertions, 2149 deletions
diff --git a/api/current.txt b/api/current.txt index d65a61dd92ca..27aa008b42a3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1605,17 +1605,17 @@ package android { field public static final int holo_purple = 17170458; // 0x106001a field public static final int holo_red_dark = 17170455; // 0x1060017 field public static final int holo_red_light = 17170454; // 0x1060016 - field public static final int primary_text_dark = 17170433; // 0x1060001 - field public static final int primary_text_dark_nodisable = 17170434; // 0x1060002 - field public static final int primary_text_light = 17170435; // 0x1060003 - field public static final int primary_text_light_nodisable = 17170436; // 0x1060004 - field public static final int secondary_text_dark = 17170437; // 0x1060005 - field public static final int secondary_text_dark_nodisable = 17170438; // 0x1060006 - field public static final int secondary_text_light = 17170439; // 0x1060007 - field public static final int secondary_text_light_nodisable = 17170440; // 0x1060008 + field public static final deprecated int primary_text_dark = 17170433; // 0x1060001 + field public static final deprecated int primary_text_dark_nodisable = 17170434; // 0x1060002 + field public static final deprecated int primary_text_light = 17170435; // 0x1060003 + field public static final deprecated int primary_text_light_nodisable = 17170436; // 0x1060004 + field public static final deprecated int secondary_text_dark = 17170437; // 0x1060005 + field public static final deprecated int secondary_text_dark_nodisable = 17170438; // 0x1060006 + field public static final deprecated int secondary_text_light = 17170439; // 0x1060007 + field public static final deprecated int secondary_text_light_nodisable = 17170440; // 0x1060008 field public static final int tab_indicator_text = 17170441; // 0x1060009 - field public static final int tertiary_text_dark = 17170448; // 0x1060010 - field public static final int tertiary_text_light = 17170449; // 0x1060011 + field public static final deprecated int tertiary_text_dark = 17170448; // 0x1060010 + field public static final deprecated int tertiary_text_light = 17170449; // 0x1060011 field public static final int transparent = 17170445; // 0x106000d field public static final int white = 17170443; // 0x106000b field public static final int widget_edittext_dark = 17170442; // 0x106000a @@ -6468,7 +6468,6 @@ package android.app.admin { method public java.lang.CharSequence getOrganizationName(android.content.ComponentName); method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName); method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName); - method public java.lang.String getPasswordBlacklistName(android.content.ComponentName); method public long getPasswordExpiration(android.content.ComponentName); method public long getPasswordExpirationTimeout(android.content.ComponentName); method public int getPasswordHistoryLength(android.content.ComponentName); @@ -6577,7 +6576,6 @@ package android.app.admin { method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence); method public void setOverrideApnsEnabled(android.content.ComponentName, boolean); method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean); - method public boolean setPasswordBlacklist(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>); method public void setPasswordExpirationTimeout(android.content.ComponentName, long); method public void setPasswordHistoryLength(android.content.ComponentName, int); method public void setPasswordMinimumLength(android.content.ComponentName, int); @@ -13655,17 +13653,17 @@ package android.graphics { method public boolean isDecodeAsAlphaMaskEnabled(); method public boolean isMutableRequired(); method public boolean isUnpremultipliedRequired(); - method public android.graphics.ImageDecoder setAllocator(int); - method public android.graphics.ImageDecoder setConserveMemory(boolean); - method public android.graphics.ImageDecoder setCrop(android.graphics.Rect); - method public android.graphics.ImageDecoder setDecodeAsAlphaMaskEnabled(boolean); - method public android.graphics.ImageDecoder setMutableRequired(boolean); - method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); - method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor); - method public android.graphics.ImageDecoder setTargetColorSpace(android.graphics.ColorSpace); - method public android.graphics.ImageDecoder setTargetSampleSize(int); - method public android.graphics.ImageDecoder setTargetSize(int, int); - method public android.graphics.ImageDecoder setUnpremultipliedRequired(boolean); + method public void setAllocator(int); + method public void setConserveMemory(boolean); + method public void setCrop(android.graphics.Rect); + method public void setDecodeAsAlphaMaskEnabled(boolean); + method public void setMutableRequired(boolean); + method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); + method public void setPostProcessor(android.graphics.PostProcessor); + method public void setTargetColorSpace(android.graphics.ColorSpace); + method public void setTargetSampleSize(int); + method public void setTargetSize(int, int); + method public void setUnpremultipliedRequired(boolean); field public static final int ALLOCATOR_DEFAULT = 0; // 0x0 field public static final int ALLOCATOR_HARDWARE = 3; // 0x3 field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2 @@ -21969,6 +21967,7 @@ package android.media { } public final class AudioDeviceInfo { + method public java.lang.String getAddress(); method public int[] getChannelCounts(); method public int[] getChannelIndexMasks(); method public int[] getChannelMasks(); @@ -24460,6 +24459,7 @@ package android.media { } public final class MicrophoneInfo { + method public java.lang.String getAddress(); method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping(); method public java.lang.String getDescription(); method public int getDirectionality(); @@ -27016,8 +27016,8 @@ package android.net { public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { method public void close() throws java.io.IOException; + method public java.io.FileDescriptor getFileDescriptor(); method public int getPort(); - method public java.io.FileDescriptor getSocket(); } public final class IpSecTransform implements java.lang.AutoCloseable { @@ -27246,6 +27246,8 @@ package android.net { public class NetworkRequest implements android.os.Parcelable { method public int describeContents(); + method public boolean hasCapability(int); + method public boolean hasTransport(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; } @@ -38568,6 +38570,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(); @@ -38595,6 +38598,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); @@ -38687,6 +38691,7 @@ package android.security.keystore { 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(); @@ -38706,6 +38711,7 @@ package android.security.keystore { 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); diff --git a/api/system-current.txt b/api/system-current.txt index ed763babb7ef..165c6ae1e628 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3105,6 +3105,10 @@ package android.net { field public static final int ERROR_INVALID_NETWORK = 1; // 0x1 } + public final class NetworkCapabilities implements android.os.Parcelable { + field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 + } + public class NetworkKey implements android.os.Parcelable { ctor public NetworkKey(android.net.WifiKey); method public int describeContents(); @@ -5552,6 +5556,7 @@ package android.telephony.euicc { package android.telephony.ims { public final class ImsCallForwardInfo implements android.os.Parcelable { + ctor public ImsCallForwardInfo(int, int, int, int, java.lang.String, int); method public int describeContents(); method public int getCondition(); method public java.lang.String getNumber(); @@ -5866,7 +5871,7 @@ package android.telephony.ims { } public final class ImsSsData implements android.os.Parcelable { - ctor public ImsSsData(); + ctor public ImsSsData(int, int, int, int, int); method public int describeContents(); method public boolean isTypeBarring(); method public boolean isTypeCf(); @@ -5917,7 +5922,7 @@ package android.telephony.ims { } public final class ImsSsInfo implements android.os.Parcelable { - ctor public ImsSsInfo(); + ctor public ImsSsInfo(int, java.lang.String); method public int describeContents(); method public java.lang.String getIcbNum(); method public int getStatus(); diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index 2b85ec08f9a6..c6e561f84052 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -34,11 +34,11 @@ FdBuffer::FdBuffer() FdBuffer::~FdBuffer() {} -status_t FdBuffer::read(unique_fd* fd, int64_t timeout) { - struct pollfd pfds = {.fd = fd->get(), .events = POLLIN}; +status_t FdBuffer::read(int fd, int64_t timeout) { + struct pollfd pfds = {.fd = fd, .events = POLLIN}; mStartTime = uptimeMillis(); - fcntl(fd->get(), F_SETFL, fcntl(fd->get(), F_GETFL, 0) | O_NONBLOCK); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); while (true) { if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { @@ -67,16 +67,16 @@ status_t FdBuffer::read(unique_fd* fd, int64_t timeout) { VLOG("return event has error %s", strerror(errno)); return errno != 0 ? -errno : UNKNOWN_ERROR; } else { - ssize_t amt = ::read(fd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite()); + ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()); if (amt < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; } else { - VLOG("Fail to read %d: %s", fd->get(), strerror(errno)); + VLOG("Fail to read %d: %s", fd, strerror(errno)); return -errno; } } else if (amt == 0) { - VLOG("Reached EOF of fd=%d", fd->get()); + VLOG("Reached EOF of fd=%d", fd); break; } mBuffer.wp()->move(amt); @@ -87,7 +87,7 @@ status_t FdBuffer::read(unique_fd* fd, int64_t timeout) { return NO_ERROR; } -status_t FdBuffer::readFully(unique_fd* fd) { +status_t FdBuffer::readFully(int fd) { mStartTime = uptimeMillis(); while (true) { @@ -99,10 +99,10 @@ status_t FdBuffer::readFully(unique_fd* fd) { } if (mBuffer.writeBuffer() == NULL) return NO_MEMORY; - ssize_t amt = TEMP_FAILURE_RETRY( - ::read(fd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite())); + ssize_t amt = + TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite())); if (amt < 0) { - VLOG("Fail to read %d: %s", fd->get(), strerror(errno)); + VLOG("Fail to read %d: %s", fd, strerror(errno)); return -errno; } else if (amt == 0) { VLOG("Done reading %zu bytes", mBuffer.size()); @@ -116,20 +116,20 @@ status_t FdBuffer::readFully(unique_fd* fd) { return NO_ERROR; } -status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, unique_fd* fromFd, +status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd, int64_t timeoutMs, const bool isSysfs) { struct pollfd pfds[] = { - {.fd = fd->get(), .events = POLLIN}, - {.fd = toFd->get(), .events = POLLOUT}, - {.fd = fromFd->get(), .events = POLLIN}, + {.fd = fd, .events = POLLIN}, + {.fd = toFd.get(), .events = POLLOUT}, + {.fd = fromFd.get(), .events = POLLIN}, }; mStartTime = uptimeMillis(); // mark all fds non blocking - fcntl(fd->get(), F_SETFL, fcntl(fd->get(), F_GETFL, 0) | O_NONBLOCK); - fcntl(toFd->get(), F_SETFL, fcntl(toFd->get(), F_GETFL, 0) | O_NONBLOCK); - fcntl(fromFd->get(), F_SETFL, fcntl(fromFd->get(), F_GETFL, 0) | O_NONBLOCK); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + fcntl(toFd.get(), F_SETFL, fcntl(toFd.get(), F_GETFL, 0) | O_NONBLOCK); + fcntl(fromFd.get(), F_SETFL, fcntl(fromFd.get(), F_GETFL, 0) | O_NONBLOCK); // A circular buffer holds data read from fd and writes to parsing process uint8_t cirBuf[BUFFER_SIZE]; @@ -166,10 +166,10 @@ status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, uni for (int i = 0; i < 3; ++i) { if ((pfds[i].revents & POLLERR) != 0) { if (i == 0 && isSysfs) { - VLOG("fd %d is sysfs, ignore its POLLERR return value", fd->get()); + VLOG("fd %d is sysfs, ignore its POLLERR return value", fd); continue; } - VLOG("fd[%d]=%d returns error events: %s", i, fd->get(), strerror(errno)); + VLOG("fd[%d]=%d returns error events: %s", i, fd, strerror(errno)); return errno != 0 ? -errno : UNKNOWN_ERROR; } } @@ -178,17 +178,17 @@ status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, uni if (cirSize != BUFFER_SIZE && pfds[0].fd != -1) { ssize_t amt; if (rpos >= wpos) { - amt = ::read(fd->get(), cirBuf + rpos, BUFFER_SIZE - rpos); + amt = ::read(fd, cirBuf + rpos, BUFFER_SIZE - rpos); } else { - amt = ::read(fd->get(), cirBuf + rpos, wpos - rpos); + amt = ::read(fd, cirBuf + rpos, wpos - rpos); } if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - VLOG("Fail to read fd %d: %s", fd->get(), strerror(errno)); + VLOG("Fail to read fd %d: %s", fd, strerror(errno)); return -errno; } // otherwise just continue } else if (amt == 0) { - VLOG("Reached EOF of input file %d", fd->get()); + VLOG("Reached EOF of input file %d", fd); pfds[0].fd = -1; // reach EOF so don't have to poll pfds[0]. } else { rpos += amt; @@ -200,13 +200,13 @@ status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, uni if (cirSize > 0 && pfds[1].fd != -1) { ssize_t amt; if (rpos > wpos) { - amt = ::write(toFd->get(), cirBuf + wpos, rpos - wpos); + amt = ::write(toFd.get(), cirBuf + wpos, rpos - wpos); } else { - amt = ::write(toFd->get(), cirBuf + wpos, BUFFER_SIZE - wpos); + amt = ::write(toFd.get(), cirBuf + wpos, BUFFER_SIZE - wpos); } if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - VLOG("Fail to write toFd %d: %s", toFd->get(), strerror(errno)); + VLOG("Fail to write toFd.get() %d: %s", toFd.get(), strerror(errno)); return -errno; } // otherwise just continue } else { @@ -217,8 +217,8 @@ status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, uni // if buffer is empty and fd is closed, close write fd. if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) { - VLOG("Close write pipe %d", toFd->get()); - toFd->reset(); + VLOG("Close write pipe %d", toFd.get()); + toFd.reset(); pfds[1].fd = -1; } @@ -231,14 +231,14 @@ status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, uni } // read from parsing process - ssize_t amt = ::read(fromFd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite()); + ssize_t amt = ::read(fromFd.get(), mBuffer.writeBuffer(), mBuffer.currentToWrite()); if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - VLOG("Fail to read fromFd %d: %s", fromFd->get(), strerror(errno)); + VLOG("Fail to read fromFd.get() %d: %s", fromFd.get(), strerror(errno)); return -errno; } // otherwise just continue } else if (amt == 0) { - VLOG("Reached EOF of fromFd %d", fromFd->get()); + VLOG("Reached EOF of fromFd.get() %d", fromFd.get()); break; } else { mBuffer.wp()->move(amt); diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h index db3a74b78178..f467da866024 100644 --- a/cmds/incidentd/src/FdBuffer.h +++ b/cmds/incidentd/src/FdBuffer.h @@ -40,13 +40,13 @@ public: * Returns NO_ERROR if there were no errors or if we timed out. * Will mark the file O_NONBLOCK. */ - status_t read(unique_fd* fd, int64_t timeoutMs); + status_t read(int fd, int64_t timeoutMs); /** * Read the data until we hit eof. * Returns NO_ERROR if there were no errors. */ - status_t readFully(unique_fd* fd); + status_t readFully(int fd); /** * Read processed results by streaming data to a parsing process, e.g. incident helper. @@ -58,8 +58,8 @@ public: * * Poll will return POLLERR if fd is from sysfs, handle this edge case. */ - status_t readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, unique_fd* fromFd, - int64_t timeoutMs, const bool isSysfs = false); + status_t readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd, int64_t timeoutMs, + const bool isSysfs = false); /** * Whether we timed out. diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index aeccefdd15c0..d02b4dd99067 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -352,8 +352,7 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str printPrivacy(p, out, String8("")); } else if (opt == "parse") { FdBuffer buf; - unique_fd infd(fileno(in)); - status_t error = buf.read(&infd, 60000); + status_t error = buf.read(fileno(in), 60000); if (error != NO_ERROR) { fprintf(err, "Error reading from stdin\n"); return error; diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 3b57d34c1ea3..3f693fad4d46 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -264,8 +264,9 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { } // parent process - status_t readStatus = buffer.readProcessedDataInStream( - &fd, &p2cPipe.writeFd(), &c2pPipe.readFd(), this->timeoutMs, mIsSysfs); + status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), + this->timeoutMs, mIsSysfs); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s", @@ -319,8 +320,10 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { index++; // look at the next file. } VLOG("GZipSection is using file %s, fd=%d", mFilenames[index], fd.get()); - if (fd.get() == -1) return -1; - + if (fd.get() == -1) { + ALOGW("GZipSection %s can't open all the files", this->name.string()); + return NO_ERROR; // e.g. LAST_KMSG will reach here in user build. + } FdBuffer buffer; Fpipe p2cPipe; Fpipe c2pPipe; @@ -354,9 +357,9 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { VLOG("GZipSection '%s' editPos=%zd, dataBeginAt=%zd", this->name.string(), editPos, dataBeginAt); - status_t readStatus = - buffer.readProcessedDataInStream(&fd, &p2cPipe.writeFd(), &c2pPipe.readFd(), - this->timeoutMs, isSysfs(mFilenames[index])); + status_t readStatus = buffer.readProcessedDataInStream( + fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs, + isSysfs(mFilenames[index])); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("GZipSection '%s' failed to read data from gzip: %s, timedout: %s", @@ -466,7 +469,7 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { pthread_attr_destroy(&attr); // Loop reading until either the timeout or the worker side is done (i.e. eof). - err = buffer.read(&data->pipe.readFd(), this->timeoutMs); + err = buffer.read(data->pipe.readFd().get(), this->timeoutMs); if (err != NO_ERROR) { // TODO: Log this error into the incident report. ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(), @@ -573,7 +576,7 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const { } cmdPipe.writeFd().reset(); - status_t readStatus = buffer.read(&ihPipe.readFd(), this->timeoutMs); + status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s", this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); @@ -892,7 +895,7 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const { // Parent process. // Read from the pipe concurrently to avoid blocking the child. FdBuffer buffer; - err = buffer.readFully(&dumpPipe.readFd()); + err = buffer.readFully(dumpPipe.readFd().get()); if (err != NO_ERROR) { ALOGW("TombstoneSection '%s' failed to read stack dump: %d", this->name.string(), err); dumpPipe.readFd().reset(); diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp index bf770173793f..7a05d7e071d0 100644 --- a/cmds/incidentd/tests/FdBuffer_test.cpp +++ b/cmds/incidentd/tests/FdBuffer_test.cpp @@ -37,7 +37,6 @@ class FdBufferTest : public Test { public: virtual void SetUp() override { ASSERT_NE(tf.fd, -1); - tffd.reset(tf.fd); ASSERT_NE(p2cPipe.init(), -1); ASSERT_NE(c2pPipe.init(), -1); } @@ -57,13 +56,13 @@ public: EXPECT_EQ(expected[i], '\0'); } - bool DoDataStream(unique_fd* rFd, unique_fd* wFd) { + bool DoDataStream(const unique_fd& rFd, const unique_fd& wFd) { char buf[BUFFER_SIZE]; ssize_t nRead; - while ((nRead = read(rFd->get(), buf, BUFFER_SIZE)) > 0) { + while ((nRead = read(rFd.get(), buf, BUFFER_SIZE)) > 0) { ssize_t nWritten = 0; while (nWritten < nRead) { - ssize_t amt = write(wFd->get(), buf + nWritten, nRead - nWritten); + ssize_t amt = write(wFd.get(), buf + nWritten, nRead - nWritten); if (amt < 0) { return false; } @@ -76,7 +75,6 @@ public: protected: FdBuffer buffer; TemporaryFile tf; - unique_fd tffd; Fpipe p2cPipe; Fpipe c2pPipe; @@ -87,7 +85,7 @@ protected: TEST_F(FdBufferTest, ReadAndWrite) { std::string testdata = "FdBuffer test string"; ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); - ASSERT_EQ(NO_ERROR, buffer.read(&tffd, READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); AssertBufferReadSuccessful(testdata.size()); AssertBufferContent(testdata.c_str()); } @@ -100,7 +98,7 @@ TEST_F(FdBufferTest, IterateEmpty) { TEST_F(FdBufferTest, ReadAndIterate) { std::string testdata = "FdBuffer test string"; ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); - ASSERT_EQ(NO_ERROR, buffer.read(&tffd, READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); int i = 0; EncodedBuffer::iterator it = buffer.data(); @@ -128,7 +126,7 @@ TEST_F(FdBufferTest, ReadTimeout) { } else { c2pPipe.writeFd().reset(); - status_t status = buffer.read(&c2pPipe.readFd(), QUICK_TIMEOUT_MS); + status_t status = buffer.read(c2pPipe.readFd().get(), QUICK_TIMEOUT_MS); ASSERT_EQ(NO_ERROR, status); EXPECT_TRUE(buffer.timedOut()); @@ -148,7 +146,7 @@ TEST_F(FdBufferTest, ReadInStreamAndWrite) { p2cPipe.writeFd().reset(); c2pPipe.readFd().reset(); ASSERT_TRUE(WriteStringToFd(HEAD, c2pPipe.writeFd())); - ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd())); + ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); // Must exit here otherwise the child process will continue executing the test binary. @@ -157,8 +155,9 @@ TEST_F(FdBufferTest, ReadInStreamAndWrite) { p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(), - &c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, + buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), READ_TIMEOUT)); AssertBufferReadSuccessful(HEAD.size() + testdata.size()); AssertBufferContent(expected.c_str()); wait(&pid); @@ -189,8 +188,9 @@ TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) { p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(), - &c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, + buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), READ_TIMEOUT)); AssertBufferReadSuccessful(HEAD.size() + testdata.size()); AssertBufferContent(expected.c_str()); wait(&pid); @@ -206,7 +206,7 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { if (pid == 0) { p2cPipe.writeFd().reset(); c2pPipe.readFd().reset(); - ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd())); + ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); _exit(EXIT_SUCCESS); @@ -214,8 +214,9 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(), - &c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, + buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), READ_TIMEOUT)); AssertBufferReadSuccessful(0); AssertBufferContent(""); wait(&pid); @@ -233,7 +234,7 @@ TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { if (pid == 0) { p2cPipe.writeFd().reset(); c2pPipe.readFd().reset(); - ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd())); + ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); _exit(EXIT_SUCCESS); @@ -241,8 +242,9 @@ TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&fd, &p2cPipe.writeFd(), - &c2pPipe.readFd(), READ_TIMEOUT)); + ASSERT_EQ(NO_ERROR, + buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), READ_TIMEOUT)); EXPECT_EQ(buffer.size(), fourMB); EXPECT_FALSE(buffer.timedOut()); EXPECT_TRUE(buffer.truncated()); @@ -278,8 +280,9 @@ TEST_F(FdBufferTest, ReadInStreamTimeOut) { p2cPipe.readFd().reset(); c2pPipe.writeFd().reset(); - ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(), - &c2pPipe.readFd(), QUICK_TIMEOUT_MS)); + ASSERT_EQ(NO_ERROR, + buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), QUICK_TIMEOUT_MS)); EXPECT_TRUE(buffer.timedOut()); kill(pid, SIGKILL); // reap the child process } diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp index 5edc0c79785b..10c2981d6403 100644 --- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp +++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp @@ -38,7 +38,7 @@ const uint8_t STRING_TYPE = 9; const uint8_t MESSAGE_TYPE = 11; const string STRING_FIELD_0 = "\x02\viamtestdata"; const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 -const string STRING_FIELD_2 = "\x12\vwhatthefuck"; +const string STRING_FIELD_2 = "\x12\vandroidwins"; const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2; @@ -58,8 +58,7 @@ public: void writeToFdBuffer(string str) { ASSERT_TRUE(WriteStringToFile(str, tf.path)); - unique_fd tffd(tf.fd); - ASSERT_EQ(NO_ERROR, buffer.read(&tffd, 10000)); + ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000)); ASSERT_EQ(str.size(), buffer.size()); } @@ -274,4 +273,4 @@ TEST_F(PrivacyBufferTest, AutoMessage) { autoMsg->children = list; string expected = "\x2a\xd" + STRING_FIELD_2; assertStripByFields(DEST_AUTOMATIC, expected, 1, autoMsg); -}
\ No newline at end of file +} diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index f93839b62dcf..2f6698bc3116 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -19,6 +19,7 @@ #include <android-base/file.h> #include <android-base/test_utils.h> #include <android/os/IncidentReportArgs.h> +#include <android/util/protobuf.h> #include <frameworks/base/libs/incident/proto/android/os/header.pb.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -31,12 +32,13 @@ const int REVERSE_PARSER = 1; const int QUICK_TIMEOUT_MS = 100; const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 -const string STRING_FIELD_2 = "\x12\vwhatthefuck"; +const string STRING_FIELD_2 = "\x12\vandroidwins"; const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 using namespace android::base; using namespace android::binder; using namespace android::os; +using namespace android::util; using namespace std; using ::testing::StrEq; using ::testing::Test; @@ -154,17 +156,26 @@ TEST_F(SectionTest, GZipSection) { requests.setMainDest(android::os::DEST_LOCAL); ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); - std::string expect, gzFile, actual; + std::string expected, gzFile, actual; ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile)); ASSERT_TRUE(ReadFileToString(tf.path, &actual)); - expect = "\x2\xC6\x6\n\"" + testFile + "\x12\x9F\x6" + gzFile; - EXPECT_THAT(actual, StrEq(expect)); + // generates the expected protobuf result. + size_t fileLen = testFile.size(); + size_t totalLen = 1 + get_varint_size(fileLen) + fileLen + 3 + gzFile.size(); + uint8_t header[20]; + header[0] = '\x2'; // header 0 << 3 + 2 + uint8_t* ptr = write_raw_varint(header + 1, totalLen); + *ptr = '\n'; // header 1 << 3 + 2 + ptr = write_raw_varint(++ptr, fileLen); + expected.assign((const char*)header, ptr - header); + expected += testFile + "\x12\x9F\x6" + gzFile; + EXPECT_THAT(actual, StrEq(expected)); } TEST_F(SectionTest, GZipSectionNoFileFound) { GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL); requests.setMainFd(STDOUT_FILENO); - ASSERT_EQ(-1, gs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); } TEST_F(SectionTest, CommandSectionConstructor) { diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp index b67764bd5130..50b05cd5b1d8 100644 --- a/cmds/statsd/benchmark/metric_util.cpp +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -127,25 +127,25 @@ AtomMatcher CreateSyncEndAtomMatcher() { } AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( - const string& name, ActivityForegroundStateChanged::Activity activity) { + const string& name, ActivityForegroundStateChanged::State state) { AtomMatcher atom_matcher; atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); field_value_matcher->set_field(4); // Activity field. - field_value_matcher->set_eq_int(activity); + field_value_matcher->set_eq_int(state); return atom_matcher; } AtomMatcher CreateMoveToBackgroundAtomMatcher() { return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND); + "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND); } AtomMatcher CreateMoveToForegroundAtomMatcher() { return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND); + "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND); } Predicate CreateScheduledJobPredicate() { @@ -315,25 +315,25 @@ std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( } std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( - const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) { + const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) { auto event = std::make_unique<LogEvent>( android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); event->write(uid); event->write("pkg_name"); event->write("class_name"); - event->write(activity); + event->write(state); event->init(); return event; } std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { return CreateActivityForegroundStateChangedEvent( - uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs); + uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs); } std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { return CreateActivityForegroundStateChangedEvent( - uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs); + uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs); } std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index cfb9d87df87b..7fe8e6208e1e 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -16,7 +16,6 @@ syntax = "proto2"; -// TODO: Not the right package and class name package android.os.statsd; option java_package = "com.android.os"; option java_outer_classname = "AtomsProto"; @@ -49,7 +48,7 @@ message Atom { oneof pushed { // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. BleScanStateChanged ble_scan_state_changed = 2; - // TODO: 3 is blank, but need not be + // 3 is available for use BleScanResultReceived ble_scan_result_received = 4; SensorStateChanged sensor_state_changed = 5; GpsScanStateChanged gps_scan_state_changed = 6; @@ -60,12 +59,12 @@ message Atom { LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11; MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12; WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13; - // TODO: 14-19 are blank, but need not be + // 14 - 19 are available BatterySaverModeStateChanged battery_saver_mode_state_changed = 20; DeviceIdleModeStateChanged device_idle_mode_state_changed = 21; DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22; AudioStateChanged audio_state_changed = 23; - MediaCodecActivityChanged media_codec_activity_changed = 24; + MediaCodecStateChanged media_codec_state_changed = 24; CameraStateChanged camera_state_changed = 25; FlashlightStateChanged flashlight_state_changed = 26; UidProcessStateChanged uid_process_state_changed = 27; @@ -74,8 +73,7 @@ message Atom { BatteryLevelChanged battery_level_changed = 30; ChargingStateChanged charging_state_changed = 31; PluggedStateChanged plugged_state_changed = 32; - // TODO: 33 is blank, but is available for use. - DeviceOnStatusChanged device_on_status_changed = 34; + // 33 - 34 are available WakeupAlarmOccurred wakeup_alarm_occurred = 35; KernelWakeupReported kernel_wakeup_reported = 36; WifiLockStateChanged wifi_lock_state_changed = 37; @@ -86,12 +84,12 @@ message Atom { ActivityForegroundStateChanged activity_foreground_state_changed = 42; IsolatedUidChanged isolated_uid_changed = 43; PacketWakeupOccurred packet_wakeup_occurred = 44; - DropboxErrorChanged dropbox_error_changed = 45; + // 45 is available AnomalyDetected anomaly_detected = 46; AppBreadcrumbReported app_breadcrumb_reported = 47; - AppStartChanged app_start_changed = 48; - AppStartCancelChanged app_start_cancel_changed = 49; - AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50; + AppStartOccurred app_start_occurred = 48; + AppStartCanceled app_start_canceled = 49; + AppStartFullyDrawn app_start_fully_drawn = 50; LmkKillOccurred lmk_kill_occurred = 51; PictureInPictureStateChanged picture_in_picture_state_changed = 52; WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53; @@ -106,7 +104,7 @@ message Atom { KeyguardStateChanged keyguard_state_changed = 62; KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63; KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64; - AppDied app_died=65; + AppDied app_died = 65; ResourceConfigurationChanged resource_configuration_changed = 66; BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67; BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68; @@ -119,6 +117,12 @@ message Atom { MobileConnectionStateChanged mobile_connection_state_changed = 75; MobileRadioTechnologyChanged mobile_radio_technology_changed = 76; UsbDeviceAttached usb_device_attached = 77; + AppCrashOccurred app_crash_occurred = 78; + ANROccurred anr_occurred = 79; + WTFOccurred wtf_occurred = 80; + LowMemReported low_mem_reported = 81; + + } // Pulled events will start at field 10000. @@ -134,7 +138,7 @@ message Atom { CpuTimePerFreq cpu_time_per_freq = 10008; CpuTimePerUid cpu_time_per_uid = 10009; CpuTimePerUidFreq cpu_time_per_uid_freq = 10010; - WifiActivityEnergyInfo wifi_activity_energy_info = 10011; + WifiActivityInfo wifi_activity_info = 10011; ModemActivityInfo modem_activity_info = 10012; BluetoothActivityInfo bluetooth_activity_info = 10007; ProcessMemoryState process_memory_state = 10013; @@ -224,21 +228,19 @@ message UidProcessStateChanged { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message ProcessLifeCycleStateChanged { - // TODO: should be a string tagged w/ uid annotation optional int32 uid = 1 [(is_uid) = true]; // The process name (usually same as the app name). - optional string name = 2; + optional string process_name = 2; // What lifecycle state the process changed to. // This enum is specific to atoms.proto. - enum Event { - PROCESS_FINISHED = 0; - PROCESS_STARTED = 1; - PROCESS_CRASHED = 2; - PROCESS_ANRED = 3; + enum State { + FINISHED = 0; + STARTED = 1; + CRASHED = 2; } - optional Event event = 3; + optional State state = 3; } /** @@ -247,7 +249,6 @@ message ProcessLifeCycleStateChanged { * Logged from: * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java */ -// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). message BleScanStateChanged { repeated AttributionNode attribution_node = 1; @@ -278,7 +279,7 @@ message BleScanResultReceived { repeated AttributionNode attribution_node = 1; // Number of ble scan results returned. - optional int32 num_of_results = 2; + optional int32 num_results = 2; } /** @@ -290,7 +291,6 @@ message BleScanResultReceived { message SensorStateChanged { repeated AttributionNode attribution_node = 1; - // TODO: Is there a way to get the actual name of the sensor? // The id (int) of the sensor. optional int32 sensor_id = 2; @@ -329,7 +329,7 @@ message SyncStateChanged { repeated AttributionNode attribution_node = 1; // Name of the sync (as named in the app). Can be chosen at run-time. - optional string name = 2; + optional string sync_name = 2; enum State { OFF = 0; @@ -348,7 +348,7 @@ message ScheduledJobStateChanged { repeated AttributionNode attribution_node = 1; // Name of the job (as named in the app) - optional string name = 2; + optional string job_name = 2; enum State { FINISHED = 0; @@ -387,7 +387,7 @@ message AudioStateChanged { * Logged from: * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ -message MediaCodecActivityChanged { +message MediaCodecStateChanged { repeated AttributionNode attribution_node = 1; enum State { @@ -561,22 +561,6 @@ message PluggedStateChanged { optional android.os.BatteryPluggedStateEnum state = 1; } -// TODO: Define this more precisely. -// TODO: Log the ON state somewhere. It isn't currently logged anywhere. -/** - * Logs when the device turns off or on. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message DeviceOnStatusChanged { - enum State { - OFF = 0; - ON = 1; - } - optional State state = 1; -} - /** * Logs when an app's wakeup alarm fires. * @@ -598,8 +582,7 @@ message WakeupAlarmOccurred { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message MobileRadioPowerStateChanged { - // TODO: Add attribution instead of uid? - optional int32 uid = 1 [(is_uid) = true]; + repeated AttributionNode attribution_node = 1; // Power state, from frameworks/base/core/proto/android/telephony/enums.proto. optional android.telephony.DataConnectionPowerStateEnum state = 2; @@ -613,8 +596,7 @@ message MobileRadioPowerStateChanged { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message WifiRadioPowerStateChanged { - // TODO: Add attribution instead of uid? - optional int32 uid = 1 [(is_uid) = true]; + repeated AttributionNode attribution_node = 1; // Power state, from frameworks/base/core/proto/android/telephony/enums.proto. optional android.telephony.DataConnectionPowerStateEnum state = 2; @@ -1154,7 +1136,6 @@ message ChargeCyclesReported { message DaveyOccurred { // The UID that logged this atom. optional int32 uid = 1 [(is_uid) = true]; - ; // Amount of time it took to render the frame. Should be >=700ms. optional int64 jank_duration_millis = 2; @@ -1221,42 +1202,70 @@ message ActivityForegroundStateChanged { optional string pkg_name = 2; optional string class_name = 3; - enum Activity { - MOVE_TO_BACKGROUND = 0; - MOVE_TO_FOREGROUND = 1; + enum State { + BACKGROUND = 0; + FOREGROUND = 1; } - optional Activity activity = 4; + optional State state = 4; } /** - * Logs when an error is written to dropbox. + * Logs when an app crashes. * Logged from: * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java */ -message DropboxErrorChanged { - // The uid if available. -1 means not available. +message AppCrashOccurred { + optional int32 uid = 1 [(is_uid) = true]; + + optional string event_type = 2; + + // The name of the process. + // system_server if it is not by an app + optional string process_name = 3; + + // The pid if available. -1 means not available. + optional sint32 pid = 4; +} + +/** + * Logs when a WTF (What a Terrible Failure) happened. + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message WTFOccurred { optional int32 uid = 1 [(is_uid) = true]; - // Tag used when recording this error to dropbox. Contains data_ or system_ prefix. optional string tag = 2; // The name of the process. + // system_server if it is not by an app optional string process_name = 3; // The pid if available. -1 means not available. optional sint32 pid = 4; +} - // 1 indicates is instant app. -1 indicates Not applicable. - optional sint32 is_instant_app = 5; +/** + * Logs when system server reports low memory. + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message LowMemReported { +} + +/** + * Logs when an app ANR (App Not Responding) occurs. + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/AppErrors.java + */ +message ANROccurred { + optional int32 uid = 1 [(is_uid) = true]; - // The activity name if available. - optional string activity_name = 6; + optional string process_name = 2; - // The package name if available. - optional string package_name = 7; + optional string short_component_name = 3; - // 1 indicates in foreground. -1 indicates not available. - optional sint32 is_foreground = 8; + optional string reason = 4; } /* @@ -1299,7 +1308,7 @@ message AnomalyDetected { optional int64 alert_id = 3; } -message AppStartChanged { +message AppStartOccurred { // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -1307,7 +1316,7 @@ message AppStartChanged { optional string pkg_name = 2; enum TransitionType { - APP_START_TRANSITION_TYPE_UNKNOWN = 0; + UNKNOWN = 0; WARM = 1; HOT = 2; COLD = 3; @@ -1346,7 +1355,7 @@ message AppStartChanged { optional int32 package_optimization_compilation_reason = 15; } -message AppStartCancelChanged { +message AppStartCanceled { // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -1354,7 +1363,7 @@ message AppStartCancelChanged { optional string pkg_name = 2; enum TransitionType { - APP_START_TRANSITION_TYPE_UNKNOWN = 0; + UNKNOWN = 0; WARM = 1; HOT = 2; COLD = 3; @@ -1366,7 +1375,7 @@ message AppStartCancelChanged { optional string activity_name = 4; } -message AppStartFullyDrawnChanged { +message AppStartFullyDrawn { // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -1374,7 +1383,7 @@ message AppStartFullyDrawnChanged { optional string pkg_name = 2; enum TransitionType { - APP_START_TRANSITION_TYPE_UNKNOWN = 0; + UNKNOWN = 0; WITH_BUNDLE = 1; WITHOUT_BUNDLE = 2; } @@ -1459,8 +1468,8 @@ message ForegroundServiceStateChanged { * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java */ message IsolatedUidChanged { - // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd. // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid. + // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd. optional int32 parent_uid = 1; optional int32 isolated_uid = 2; @@ -1621,9 +1630,8 @@ message WifiBytesTransfer { message WifiBytesTransferByFgBg { optional int32 uid = 1 [(is_uid) = true]; - // 1 denotes foreground and 0 denotes background. This is called Set in - // NetworkStats. - optional int32 is_foreground = 2; + // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats. + optional bool is_foreground = 2; optional int64 rx_bytes = 3; @@ -1663,7 +1671,7 @@ message MobileBytesTransferByFgBg { // 1 denotes foreground and 0 denotes background. This is called Set in // NetworkStats. - optional int32 is_foreground = 2; + optional bool is_foreground = 2; optional int64 rx_bytes = 3; @@ -1760,7 +1768,7 @@ message CpuTimePerUidFreq { /** * Pulls Wifi Controller Activity Energy Info */ -message WifiActivityEnergyInfo { +message WifiActivityInfo { // timestamp(wall clock) of record creation optional uint64 timestamp_millis = 1; // stack reported state diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index 0e23bf01074c..2f0e88595d45 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -120,11 +120,11 @@ const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = { 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}}, // wifi_activity_energy_info - {android::util::WIFI_ACTIVITY_ENERGY_INFO, + {android::util::WIFI_ACTIVITY_INFO, {{}, {}, 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}}, + new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}}, // modem_activity_info {android::util::MODEM_ACTIVITY_INFO, {{}, diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index ef637df68ed3..22ff9428c83a 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -76,6 +76,7 @@ const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13; const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14; const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; +const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17; const int FIELD_ID_MATCHER_STATS_ID = 1; const int FIELD_ID_MATCHER_STATS_COUNT = 2; @@ -255,6 +256,20 @@ void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& i } } +void StatsdStats::noteMetricDimensionInConditionSize( + const ConfigKey& key, const int64_t& id, int size) { + lock_guard<std::mutex> lock(mLock); + // if name doesn't exist before, it will create the key with count 0. + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + auto& metricsDimensionMap = statsIt->second->metric_dimension_in_condition_stats; + if (size > metricsDimensionMap[id]) { + metricsDimensionMap[id] = size; + } +} + void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) { lock_guard<std::mutex> lock(mLock); @@ -339,6 +354,7 @@ void StatsdStats::resetInternalLocked() { config.second->matcher_stats.clear(); config.second->condition_stats.clear(); config.second->metric_stats.clear(); + config.second->metric_dimension_in_condition_stats.clear(); config.second->alert_stats.clear(); } } @@ -504,6 +520,13 @@ void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* pr proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); proto->end(tmpToken); } + for (const auto& pair : configStats.metric_dimension_in_condition_stats) { + uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); + proto->end(tmpToken); + } for (const auto& pair : configStats.alert_stats) { uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 7f8755b8c1e0..bd395c4c232b 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -57,6 +57,12 @@ struct ConfigStats { // it means some data has been dropped. The map size is capped by kMaxConfigCount. std::map<const int64_t, int> metric_stats; + // Stores the max number of output tuple of dimensions in condition across dimensions in what + // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is + // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by + // kMaxConfigCount. + std::map<const int64_t, int> metric_dimension_in_condition_stats; + // Stores the number of times an anomaly detection alert has been declared. // The map size is capped by kMaxConfigCount. std::map<const int64_t, int> alert_stats; @@ -183,6 +189,19 @@ public: */ void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size); + + /** + * Report the max size of output tuple of dimension in condition across dimensions in what. + * + * Note: only report when the metric has an output dimension in condition, and the max tuple + * count > kDimensionKeySizeSoftLimit. + * + * [key]: The config key that this metric belongs to. + * [id]: The id of the metric. + * [size]: The output tuple size. + */ + void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size); + /** * Report a matcher has been matched. * diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index c28bb8800e20..f02f30756b2d 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -308,11 +308,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool conditio if (mMetric2ConditionLinks.size() == 0 || trueDim.contains(linkedConditionDimensionKey)) { if (!whatIt.second.empty()) { + auto newEventKey = MetricDimensionKey(whatIt.first, trueDim); + if (hitGuardRailLocked(newEventKey)) { + continue; + } unique_ptr<DurationTracker> newTracker = whatIt.second.begin()->second->clone(eventTime); if (newTracker != nullptr) { - newTracker->setEventKey( - MetricDimensionKey(whatIt.first, trueDim)); + newTracker->setEventKey(newEventKey); newTracker->onConditionChanged(true, eventTime); whatIt.second[trueDim] = std::move(newTracker); } @@ -370,11 +373,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondit for (const auto& conditionDimension : conditionDimensionsKeySet) { for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { if (!whatIt.second.empty()) { + auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension); + if (hitGuardRailLocked(newEventKey)) { + continue; + } unique_ptr<DurationTracker> newTracker = whatIt.second.begin()->second->clone(eventTime); if (newTracker != nullptr) { - newTracker->setEventKey(MetricDimensionKey( - whatIt.first, conditionDimension)); + newTracker->setEventKey(MetricDimensionKey(newEventKey)); newTracker->onSlicedConditionMayChange(overallCondition, eventTime); whatIt.second[conditionDimension] = std::move(newTracker); } @@ -397,10 +403,13 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondit for (const auto& conditionDimension : conditionDimensionsKeys) { if (!whatIt.second.empty() && whatIt.second.find(conditionDimension) == whatIt.second.end()) { + auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension); + if (hitGuardRailLocked(newEventKey)) { + continue; + } auto newTracker = whatIt.second.begin()->second->clone(eventTime); if (newTracker != nullptr) { - newTracker->setEventKey( - MetricDimensionKey(whatIt.first, conditionDimension)); + newTracker->setEventKey(newEventKey); newTracker->onSlicedConditionMayChange(overallCondition, eventTime); whatIt.second[conditionDimension] = std::move(newTracker); } @@ -552,15 +561,35 @@ void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { } bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { - // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; - StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); - // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. - if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { - ALOGE("DurationMetric %lld dropping data for dimension key %s", - (long long)mMetricId, newKey.toString().c_str()); - return true; + auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat()); + if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { + auto condIt = whatIt->second.find(newKey.getDimensionKeyInCondition()); + if (condIt != whatIt->second.end()) { + return false; + } + if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = whatIt->second.size() + 1; + StatsdStats::getInstance().noteMetricDimensionInConditionSize( + mConfigKey, mMetricId, newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("DurationMetric %lld dropping data for condition dimension key %s", + (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str()); + return true; + } + } + } else { + // 1. Report the tuple count if the tuple count > soft limit + if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize( + mConfigKey, mMetricId, newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("DurationMetric %lld dropping data for what dimension key %s", + (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str()); + return true; + } } } return false; diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index dd3b37c8c2d7..a25df3fc4c09 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -241,6 +241,7 @@ message StatsdStatsReport { repeated ConditionStats condition_stats = 14; repeated MetricStats metric_stats = 15; repeated AlertStats alert_stats = 16; + repeated MetricStats metric_dimension_in_condition_stats = 17; } repeated ConfigStats config_stats = 3; diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc index 920273b5dfaa..3a0c22425e5c 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -20,5 +20,5 @@ service statsd /system/bin/statsd on post-fs-data # Create directory for statsd - mkdir /data/misc/stats-data/ 0770 statsd statsd - mkdir /data/misc/stats-service/ 0770 statsd statsd + mkdir /data/misc/stats-data/ 0770 statsd system + mkdir /data/misc/stats-service/ 0770 statsd system diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp index 73e7c44dc3da..5a6aba624d7d 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -356,7 +356,7 @@ TEST(AtomMatcherTest, TestWriteAtomToProto) { EXPECT_EQ("location1", atom.attribution_node(0).tag()); EXPECT_EQ(2222, atom.attribution_node(1).uid()); EXPECT_EQ("location2", atom.attribution_node(1).tag()); - EXPECT_EQ(999, atom.num_of_results()); + EXPECT_EQ(999, atom.num_results()); } diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp index 2e6a0f05a341..2b91324a9d19 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp @@ -34,7 +34,7 @@ StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sa *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); - auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_CHANGED); + auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_OCCURRED); *config.add_atom_matcher() = atomMatcher; auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); @@ -49,18 +49,18 @@ StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sa gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false); gaugeMetric->set_sampling_type(sampling_type); auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields(); - fieldMatcher->set_field(android::util::APP_START_CHANGED); + fieldMatcher->set_field(android::util::APP_START_OCCURRED); fieldMatcher->add_child()->set_field(3); // type (enum) fieldMatcher->add_child()->set_field(4); // activity_name(str) fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64) *gaugeMetric->mutable_dimensions_in_what() = - CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ }); + CreateDimensions(android::util::APP_START_OCCURRED, {1 /* uid field */ }); gaugeMetric->set_bucket(FIVE_MINUTES); auto links = gaugeMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(android::util::APP_START_CHANGED); + dimensionWhat->set_field(android::util::APP_START_OCCURRED); dimensionWhat->add_child()->set_field(1); // uid field. auto dimensionCondition = links->mutable_fields_in_condition(); dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); @@ -68,12 +68,12 @@ StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sa return config; } -std::unique_ptr<LogEvent> CreateAppStartChangedEvent( - const int uid, const string& pkg_name, AppStartChanged::TransitionType type, +std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( + const int uid, const string& pkg_name, AppStartOccurred::TransitionType type, const string& activity_name, const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec, uint64_t timestampNs) { auto logEvent = std::make_unique<LogEvent>( - android::util::APP_START_CHANGED, timestampNs); + android::util::APP_START_OCCURRED, timestampNs); logEvent->write(uid); logEvent->write(pkg_name); logEvent->write(type); @@ -112,32 +112,32 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100)); - events.push_back(CreateAppStartChangedEvent( - appUid1, "app1", AppStartChanged::WARM, "activity_name1", "calling_pkg_name1", + events.push_back(CreateAppStartOccurredEvent( + appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10)); - events.push_back(CreateAppStartChangedEvent( - appUid1, "app1", AppStartChanged::HOT, "activity_name2", "calling_pkg_name2", + events.push_back(CreateAppStartOccurredEvent( + appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20)); - events.push_back(CreateAppStartChangedEvent( - appUid1, "app1", AppStartChanged::COLD, "activity_name3", "calling_pkg_name3", + events.push_back(CreateAppStartOccurredEvent( + appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30)); - events.push_back(CreateAppStartChangedEvent( - appUid1, "app1", AppStartChanged::WARM, "activity_name4", "calling_pkg_name4", + events.push_back(CreateAppStartOccurredEvent( + appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4", true /*is_instant_app*/, 104 /*activity_start_msec*/, bucketStartTimeNs + bucketSizeNs + 30)); - events.push_back(CreateAppStartChangedEvent( - appUid1, "app1", AppStartChanged::COLD, "activity_name5", "calling_pkg_name5", + events.push_back(CreateAppStartOccurredEvent( + appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5", true /*is_instant_app*/, 105 /*activity_start_msec*/, bucketStartTimeNs + 2 * bucketSizeNs)); - events.push_back(CreateAppStartChangedEvent( - appUid1, "app1", AppStartChanged::HOT, "activity_name6", "calling_pkg_name6", + events.push_back(CreateAppStartOccurredEvent( + appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6", false /*is_instant_app*/, 106 /*activity_start_msec*/, bucketStartTimeNs + 2 * bucketSizeNs + 10)); events.push_back(CreateMoveToBackgroundEvent( appUid2, bucketStartTimeNs + bucketSizeNs + 10)); - events.push_back(CreateAppStartChangedEvent( - appUid2, "app2", AppStartChanged::COLD, "activity_name7", "calling_pkg_name7", + events.push_back(CreateAppStartOccurredEvent( + appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7", true /*is_instant_app*/, 201 /*activity_start_msec*/, bucketStartTimeNs + 2 * bucketSizeNs + 10)); @@ -159,7 +159,7 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(2, gaugeMetrics.data_size()); auto data = gaugeMetrics.data(0); - EXPECT_EQ(android::util::APP_START_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field()); EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); EXPECT_EQ(1 /* uid field */, data.dimensions_in_what().value_tuple().dimensions_value(0).field()); @@ -171,29 +171,29 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(2, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::HOT, data.bucket_info(0).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name2", - data.bucket_info(0).atom(0).app_start_changed().activity_name()); + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); EXPECT_EQ(102L, - data.bucket_info(0).atom(0).app_start_changed().activity_start_millis()); - EXPECT_EQ(AppStartChanged::COLD, - data.bucket_info(0).atom(1).app_start_changed().type()); + data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(0).atom(1).app_start_occurred().type()); EXPECT_EQ("activity_name3", - data.bucket_info(0).atom(1).app_start_changed().activity_name()); + data.bucket_info(0).atom(1).app_start_occurred().activity_name()); EXPECT_EQ(103L, - data.bucket_info(0).atom(1).app_start_changed().activity_start_millis()); + data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis()); EXPECT_EQ(1, data.bucket_info(1).atom_size()); EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::WARM, - data.bucket_info(1).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::WARM, + data.bucket_info(1).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name4", - data.bucket_info(1).atom(0).app_start_changed().activity_name()); + data.bucket_info(1).atom(0).app_start_occurred().activity_name()); EXPECT_EQ(104L, - data.bucket_info(1).atom(0).app_start_changed().activity_start_millis()); + data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); EXPECT_EQ(2, data.bucket_info(2).atom_size()); EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size()); @@ -202,41 +202,41 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { data.bucket_info(2).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(2).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::COLD, - data.bucket_info(2).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(2).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name5", - data.bucket_info(2).atom(0).app_start_changed().activity_name()); + data.bucket_info(2).atom(0).app_start_occurred().activity_name()); EXPECT_EQ(105L, - data.bucket_info(2).atom(0).app_start_changed().activity_start_millis()); - EXPECT_EQ(AppStartChanged::HOT, - data.bucket_info(2).atom(1).app_start_changed().type()); + data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); + EXPECT_EQ(AppStartOccurred::HOT, + data.bucket_info(2).atom(1).app_start_occurred().type()); EXPECT_EQ("activity_name6", - data.bucket_info(2).atom(1).app_start_changed().activity_name()); + data.bucket_info(2).atom(1).app_start_occurred().activity_name()); EXPECT_EQ(106L, - data.bucket_info(2).atom(1).app_start_changed().activity_start_millis()); + data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis()); } else { EXPECT_EQ(1, data.bucket_info(0).atom_size()); EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size()); EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::HOT, data.bucket_info(0).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name2", - data.bucket_info(0).atom(0).app_start_changed().activity_name()); + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); EXPECT_EQ(102L, - data.bucket_info(0).atom(0).app_start_changed().activity_start_millis()); + data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); EXPECT_EQ(1, data.bucket_info(1).atom_size()); EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size()); EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::WARM, - data.bucket_info(1).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::WARM, + data.bucket_info(1).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name4", - data.bucket_info(1).atom(0).app_start_changed().activity_name()); + data.bucket_info(1).atom(0).app_start_occurred().activity_name()); EXPECT_EQ(104L, - data.bucket_info(1).atom(0).app_start_changed().activity_start_millis()); + data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis()); EXPECT_EQ(1, data.bucket_info(2).atom_size()); EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size()); @@ -245,17 +245,17 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { data.bucket_info(2).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(2).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::COLD, - data.bucket_info(2).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::COLD, + data.bucket_info(2).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name5", - data.bucket_info(2).atom(0).app_start_changed().activity_name()); + data.bucket_info(2).atom(0).app_start_occurred().activity_name()); EXPECT_EQ(105L, - data.bucket_info(2).atom(0).app_start_changed().activity_start_millis()); + data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis()); } data = gaugeMetrics.data(1); - EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(1 /* uid field */, data.dimensions_in_what().value_tuple().dimensions_value(0).field()); @@ -266,10 +266,10 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size()); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_nanos()); EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_nanos()); - EXPECT_EQ(AppStartChanged::COLD, data.bucket_info(0).atom(0).app_start_changed().type()); + EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type()); EXPECT_EQ("activity_name7", - data.bucket_info(0).atom(0).app_start_changed().activity_name()); - EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_changed().activity_start_millis()); + data.bucket_info(0).atom(0).app_start_occurred().activity_name()); + EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis()); } } diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 5c4eda8348d1..04ce73a76d3b 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -210,7 +210,7 @@ TEST(StatsdStatsTest, TestAtomLog) { stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1); stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2); - stats.noteAtomLogged(android::util::DROPBOX_ERROR_CHANGED, now + 3); + stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3); // pulled event, should ignore stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFER, now + 4); @@ -228,7 +228,7 @@ TEST(StatsdStatsTest, TestAtomLog) { if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) { sensorAtomGood = true; } - if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) { + if (atomStats.tag() == android::util::APP_CRASH_OCCURRED && atomStats.count() == 1) { dropboxAtomGood = true; } } diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index ce44a35cbf21..66215004db5f 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -152,42 +152,42 @@ AtomMatcher CreateSyncEndAtomMatcher() { } AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( - const string& name, ActivityForegroundStateChanged::Activity activity) { + const string& name, ActivityForegroundStateChanged::State state) { AtomMatcher atom_matcher; atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); field_value_matcher->set_field(4); // Activity field. - field_value_matcher->set_eq_int(activity); + field_value_matcher->set_eq_int(state); return atom_matcher; } AtomMatcher CreateMoveToBackgroundAtomMatcher() { return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND); + "Background", ActivityForegroundStateChanged::BACKGROUND); } AtomMatcher CreateMoveToForegroundAtomMatcher() { return CreateActivityForegroundStateChangedAtomMatcher( - "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND); + "Foreground", ActivityForegroundStateChanged::FOREGROUND); } AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher( - const string& name, ProcessLifeCycleStateChanged::Event event) { + const string& name, ProcessLifeCycleStateChanged::State state) { AtomMatcher atom_matcher; atom_matcher.set_id(StringToId(name)); auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); field_value_matcher->set_field(3); // Process state field. - field_value_matcher->set_eq_int(event); + field_value_matcher->set_eq_int(state); return atom_matcher; } AtomMatcher CreateProcessCrashAtomMatcher() { return CreateProcessLifeCycleStateChangedAtomMatcher( - "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED); + "Crashed", ProcessLifeCycleStateChanged::CRASHED); } Predicate CreateScheduledJobPredicate() { @@ -241,8 +241,8 @@ Predicate CreateIsSyncingPredicate() { Predicate CreateIsInBackgroundPredicate() { Predicate predicate; predicate.set_id(StringToId("IsInBackground")); - predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground")); - predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground")); + predicate.mutable_simple_predicate()->set_start(StringToId("Background")); + predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground")); return predicate; } @@ -373,25 +373,25 @@ std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( } std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( - const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) { + const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) { auto event = std::make_unique<LogEvent>( android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); event->write(uid); event->write("pkg_name"); event->write("class_name"); - event->write(activity); + event->write(state); event->init(); return event; } std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { return CreateActivityForegroundStateChangedEvent( - uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs); + uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs); } std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { return CreateActivityForegroundStateChangedEvent( - uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs); + uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs); } std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( @@ -418,19 +418,19 @@ std::unique_ptr<LogEvent> CreateSyncEndEvent( } std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( - const int uid, const ProcessLifeCycleStateChanged::Event event, uint64_t timestampNs) { + const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) { auto logEvent = std::make_unique<LogEvent>( android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs); logEvent->write(uid); logEvent->write(""); - logEvent->write(event); + logEvent->write(state); logEvent->init(); return logEvent; } std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) { return CreateProcessLifeCycleStateChangedEvent( - uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs); + uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs); } std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( diff --git a/core/java/android/app/AppComponentFactory.java b/core/java/android/app/AppComponentFactory.java index 4df737999434..cfaeec9096ed 100644 --- a/core/java/android/app/AppComponentFactory.java +++ b/core/java/android/app/AppComponentFactory.java @@ -36,6 +36,10 @@ public class AppComponentFactory { * Allows application to override the creation of the application object. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the Application object. The returned object will not be initialized + * as a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -50,6 +54,10 @@ public class AppComponentFactory { * Allows application to override the creation of activities. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the Activity object. The returned object will not be initialized + * as a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -80,6 +88,10 @@ public class AppComponentFactory { * Allows application to override the creation of services. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the Service object. The returned object will not be initialized + * as a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -95,6 +107,10 @@ public class AppComponentFactory { * Allows application to override the creation of providers. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the ContentProvider object. The returned object will not be initialized + * with a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -108,5 +124,5 @@ public class AppComponentFactory { /** * @hide */ - public static AppComponentFactory DEFAULT = new AppComponentFactory(); + public static final AppComponentFactory DEFAULT = new AppComponentFactory(); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3c6f13570d3e..b64aae5240b2 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2747,110 +2747,6 @@ public class DevicePolicyManager { } /** - * The maximum number of characters allowed in the password blacklist. - */ - private static final int PASSWORD_BLACKLIST_CHARACTER_LIMIT = 128 * 1000; - - /** - * Throws an exception if the password blacklist is too large. - * - * @hide - */ - public static void enforcePasswordBlacklistSize(List<String> blacklist) { - if (blacklist == null) { - return; - } - long characterCount = 0; - for (final String item : blacklist) { - characterCount += item.length(); - } - if (characterCount > PASSWORD_BLACKLIST_CHARACTER_LIMIT) { - throw new IllegalArgumentException("128 thousand blacklist character limit exceeded by " - + (characterCount - PASSWORD_BLACKLIST_CHARACTER_LIMIT) + " characters"); - } - } - - /** - * Called by an application that is administering the device to blacklist passwords. - * <p> - * Any blacklisted password or PIN is prevented from being enrolled by the user or the admin. - * Note that the match against the blacklist is case insensitive. The blacklist applies for all - * password qualities requested by {@link #setPasswordQuality} however it is not taken into - * consideration by {@link #isActivePasswordSufficient}. - * <p> - * The blacklist can be cleared by passing {@code null} or an empty list. The blacklist is - * given a name that is used to track which blacklist is currently set by calling {@link - * #getPasswordBlacklistName}. If the blacklist is being cleared, the name is ignored and {@link - * #getPasswordBlacklistName} will return {@code null}. The name can only be {@code null} when - * the blacklist is being cleared. - * <p> - * The blacklist is limited to a total of 128 thousand characters rather than limiting to a - * number of entries. - * <p> - * This method can be called on the {@link DevicePolicyManager} instance returned by - * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent - * profile. - * - * @param admin the {@link DeviceAdminReceiver} this request is associated with - * @param name name to associate with the blacklist - * @param blacklist list of passwords to blacklist or {@code null} to clear the blacklist - * @return whether the new blacklist was successfully installed - * @throws SecurityException if {@code admin} is not a device or profile owner - * @throws IllegalArgumentException if the blacklist surpasses the character limit - * @throws NullPointerException if {@code name} is {@code null} when setting a non-empty list - * - * @see #getPasswordBlacklistName - * @see #isActivePasswordSufficient - * @see #resetPasswordWithToken - */ - public boolean setPasswordBlacklist(@NonNull ComponentName admin, @Nullable String name, - @Nullable List<String> blacklist) { - enforcePasswordBlacklistSize(blacklist); - - try { - return mService.setPasswordBlacklist(admin, name, blacklist, mParentInstance); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** - * Get the name of the password blacklist set by the given admin. - * - * @param admin the {@link DeviceAdminReceiver} this request is associated with - * @return the name of the blacklist or {@code null} if no blacklist is set - * - * @see #setPasswordBlacklist - */ - public @Nullable String getPasswordBlacklistName(@NonNull ComponentName admin) { - try { - return mService.getPasswordBlacklistName(admin, myUserId(), mParentInstance); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** - * Test if a given password is blacklisted. - * - * @param userId the user to valiate for - * @param password the password to check against the blacklist - * @return whether the password is blacklisted - * - * @see #setPasswordBlacklist - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.TEST_BLACKLISTED_PASSWORD) - public boolean isPasswordBlacklisted(@UserIdInt int userId, @NonNull String password) { - try { - return mService.isPasswordBlacklisted(userId, password); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** * Determine whether the current password the user has set is sufficient to meet the policy * requirements (e.g. quality, minimum length) that have been requested by the admins of this * user and its participating profiles. Restrictions on profiles that have a separate challenge diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 4b39a9a53252..37508cdc1119 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -79,10 +79,6 @@ interface IDevicePolicyManager { long getPasswordExpiration(in ComponentName who, int userHandle, boolean parent); - boolean setPasswordBlacklist(in ComponentName who, String name, in List<String> blacklist, boolean parent); - String getPasswordBlacklistName(in ComponentName who, int userId, boolean parent); - boolean isPasswordBlacklisted(int userId, String password); - boolean isActivePasswordSufficient(int userHandle, boolean parent); boolean isProfileActivePasswordSufficientForParent(int userHandle); boolean isUsingUnifiedPassword(in ComponentName admin); diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index db98ff82581c..0285e9f9cc59 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -82,6 +82,17 @@ public class SliceManager { * An activity can be statically linked to a slice uri by including a meta-data item * for this key that contains a valid slice uri for the same application declaring * the activity. + * + * <pre class="prettyprint"> + * {@literal + * <activity android:name="com.example.mypkg.MyActivity"> + * <meta-data android:name="android.metadata.SLICE_URI" + * android:value="content://com.example.mypkg/main_slice" /> + * </activity>} + * </pre> + * + * @see #mapIntentToUri(Intent) + * @see SliceProvider#onMapIntentToUri(Intent) */ public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI"; @@ -258,16 +269,17 @@ public class SliceManager { * <p> * This goes through a several stage resolution process to determine if any slice * can represent this intent. - * - If the intent contains data that {@link ContentResolver#getType} is - * {@link SliceProvider#SLICE_TYPE} then the data will be returned. - * - If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then + * <ol> + * <li> If the intent contains data that {@link ContentResolver#getType} is + * {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li> + * <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result - * will be returned. - * - Lastly, if the intent explicitly points at an activity, and that activity has + * will be returned.</li> + * <li>Lastly, if the intent explicitly points at an activity, and that activity has * meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be - * returned. - * - If no slice is found, then {@code null} is returned. - * + * returned.</li> + * <li>If no slice is found, then {@code null} is returned.</li> + * </ol> * @param intent The intent associated with a slice. * @return The Slice Uri provided by the app or null if none exists. * @see Slice diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index 57f05884ce6d..8034bb62c94c 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -56,7 +56,8 @@ public final class IpSecAlgorithm implements Parcelable { * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b> * * <p>Keys for this algorithm must be 128 bits in length. - * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128. + * + * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128. */ public static final String AUTH_HMAC_MD5 = "hmac(md5)"; @@ -65,7 +66,8 @@ public final class IpSecAlgorithm implements Parcelable { * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b> * * <p>Keys for this algorithm must be 160 bits in length. - * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160. + * + * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160. */ public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; @@ -73,7 +75,8 @@ public final class IpSecAlgorithm implements Parcelable { * SHA256 HMAC Authentication/Integrity Algorithm. * * <p>Keys for this algorithm must be 256 bits in length. - * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256. + * + * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256. */ public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; @@ -81,7 +84,8 @@ public final class IpSecAlgorithm implements Parcelable { * SHA384 HMAC Authentication/Integrity Algorithm. * * <p>Keys for this algorithm must be 384 bits in length. - * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384. + * + * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384. */ public static final String AUTH_HMAC_SHA384 = "hmac(sha384)"; @@ -89,7 +93,8 @@ public final class IpSecAlgorithm implements Parcelable { * SHA512 HMAC Authentication/Integrity Algorithm. * * <p>Keys for this algorithm must be 512 bits in length. - * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512. + * + * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512. */ public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; @@ -112,6 +117,7 @@ public final class IpSecAlgorithm implements Parcelable { AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, + AUTH_HMAC_SHA384, AUTH_HMAC_SHA512, AUTH_CRYPT_AES_GCM }) @@ -126,11 +132,14 @@ public final class IpSecAlgorithm implements Parcelable { * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are * defined as constants in this class. * + * <p>For algorithms that produce an integrity check value, the truncation length is a required + * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)} + * * @param algorithm name of the algorithm. * @param key key padded to a multiple of 8 bits. */ public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) { - this(algorithm, key, key.length * 8); + this(algorithm, key, 0); } /** @@ -228,6 +237,7 @@ public final class IpSecAlgorithm implements Parcelable { case AUTH_CRYPT_AES_GCM: // The keying material for GCM is a key plus a 32-bit salt isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32; + isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128; break; default: throw new IllegalArgumentException("Couldn't find an algorithm: " + name); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index a88fe0428a06..15255083260b 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -274,7 +274,8 @@ public final class IpSecManager { * * @param destinationAddress the destination address for traffic bearing the requested SPI. * For inbound traffic, the destination should be an address currently assigned on-device. - * @param requestedSpi the requested SPI, or '0' to allocate a random SPI + * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is + * reserved and may not be used. See RFC 4303 Section 2.1. * @return the reserved SecurityParameterIndex * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are * currently allocated for this user @@ -502,7 +503,7 @@ public final class IpSecManager { * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the * caller. The caller should not close the {@code FileDescriptor} returned by {@link - * #getSocket}, but should use {@link #close} instead. + * #getFileDescriptor}, but should use {@link #close} instead. * * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic * of the next user who binds to that port. To prevent this scenario, these sockets are held @@ -541,8 +542,8 @@ public final class IpSecManager { mCloseGuard.open("constructor"); } - /** Get the wrapped socket. */ - public FileDescriptor getSocket() { + /** Get the encapsulation socket's file descriptor. */ + public FileDescriptor getFileDescriptor() { if (mPfd == null) { return null; } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index ff5714b38212..374b3abcb641 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.net.ConnectivityManager.NetworkCallback; import android.os.Parcel; import android.os.Parcelable; @@ -276,6 +277,7 @@ public final class NetworkCapabilities implements Parcelable { * this network can be used by system apps to upload telemetry data. * @hide */ + @SystemApi public static final int NET_CAPABILITY_OEM_PAID = 22; private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 4f92fa6a7340..caefd896ef16 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -17,6 +17,8 @@ package android.net; import android.annotation.NonNull; +import android.net.NetworkCapabilities.NetCapability; +import android.net.NetworkCapabilities.Transport; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -427,6 +429,20 @@ public class NetworkRequest implements Parcelable { return type == Type.BACKGROUND_REQUEST; } + /** + * @see Builder#addCapability(int) + */ + public boolean hasCapability(@NetCapability int capability) { + return networkCapabilities.hasCapability(capability); + } + + /** + * @see Builder#addTransportType(int) + */ + public boolean hasTransport(@Transport int transportType) { + return networkCapabilities.hasTransport(transportType); + } + public String toString() { return "NetworkRequest [ " + type + " id=" + requestId + (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") + diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b7f6cdef20ed..b0367dc3f238 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11061,7 +11061,14 @@ public final class Settings { */ public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max"; - /** + /** + * See com.android.settingslib.fuelgauge.BatterySaverUtils. + * @hide + */ + public static final String LOW_POWER_MODE_SUGGESTION_PARAMS = + "low_power_mode_suggestion_params"; + + /** * If not 0, the activity manager will aggressively finish activities and * processes as soon as they are no longer needed. If 0, the normal * extended lifetime is used. 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/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java index d50424db4e56..741af1297763 100644 --- a/core/java/android/security/keystore/RecoveryController.java +++ b/core/java/android/security/keystore/RecoveryController.java @@ -443,16 +443,7 @@ public class RecoveryController { */ public byte[] generateAndStoreKey(@NonNull String alias) throws InternalRecoveryServiceException, LockScreenRequiredException { - try { - return mBinder.generateAndStoreKey(alias); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - if (e.errorCode == ERROR_INSECURE_USER) { - throw new LockScreenRequiredException(e.getMessage()); - } - throw wrapUnexpectedServiceSpecificException(e); - } + throw new UnsupportedOperationException(); } /** diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java index e46c34c85d55..9334aa99c86c 100644 --- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java +++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java @@ -78,23 +78,8 @@ public final class KeyChainSnapshot implements Parcelable { private byte[] mEncryptedRecoveryKeyBlob; /** - * @hide - * Deprecated, consider using builder. + * Use builder to create an instance of the class. */ - public KeyChainSnapshot( - int snapshotVersion, - @NonNull List<KeyChainProtectionParams> keyChainProtectionParams, - @NonNull List<WrappedApplicationKey> wrappedApplicationKeys, - @NonNull byte[] encryptedRecoveryKeyBlob) { - mSnapshotVersion = snapshotVersion; - mKeyChainProtectionParams = - Preconditions.checkCollectionElementsNotNull(keyChainProtectionParams, - "KeyChainProtectionParams"); - mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys, - "wrappedApplicationKeys"); - mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob); - } - private KeyChainSnapshot() { } @@ -108,7 +93,7 @@ public final class KeyChainSnapshot implements Parcelable { } /** - * Number of user secret guesses allowed during Keychain recovery. + * Number of user secret guesses allowed during KeyChain recovery. */ public int getMaxAttempts() { return mMaxAttempts; diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java index fd80bb0bbe57..5165f0c30a36 100644 --- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java +++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java @@ -106,7 +106,7 @@ public final class KeyDerivationParams implements Parcelable { /** * @hide */ - KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt, + private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt, int memoryDifficulty) { mAlgorithm = algorithm; mSalt = Preconditions.checkNotNull(salt); diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index ca5f967237e8..a006fa6ab2ef 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -577,16 +577,7 @@ public class RecoveryController { @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) public byte[] generateAndStoreKey(@NonNull String alias, byte[] account) throws InternalRecoveryServiceException, LockScreenRequiredException { - try { - return mBinder.generateAndStoreKey(alias); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - if (e.errorCode == ERROR_INSECURE_USER) { - throw new LockScreenRequiredException(e.getMessage()); - } - throw wrapUnexpectedServiceSpecificException(e); - } + throw new UnsupportedOperationException("Operation is not supported, use generateKey"); } /** diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java index 0690bd5fb612..80845d9e0491 100644 --- a/core/java/android/security/keystore/recovery/RecoverySession.java +++ b/core/java/android/security/keystore/recovery/RecoverySession.java @@ -157,8 +157,8 @@ public class RecoverySession implements AutoCloseable { * @param vaultChallenge Data passed from server for this recovery session and used to prevent * replay attacks. * @param secrets Secrets provided by user, the method only uses type and secret fields. - * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is - * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric + * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey + * and contains a proof of user secrets possession, session symmetric * key and parameters necessary to identify the counter with the number of failed recovery * attempts. * @throws CertificateException if the {@code verifierCertPath} is invalid. @@ -228,7 +228,8 @@ public class RecoverySession implements AutoCloseable { * * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob - * and session. + * and session key generated by {@link #start}. + * @return {@code Map} from recovered keys aliases to their references. * @throws SessionExpiredException if {@code session} has since been closed. * @throws DecryptionFailedException if unable to decrypt the snapshot. * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. @@ -288,8 +289,7 @@ public class RecoverySession implements AutoCloseable { } /** - * Deletes all data associated with {@code session}. Should not be invoked directly but via - * {@link RecoverySession#close()}. + * Deletes all data associated with {@code session}. */ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) @Override diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java index 8a7ff4da26e3..01287104f9b2 100644 --- a/core/java/android/service/notification/ScheduleCalendar.java +++ b/core/java/android/service/notification/ScheduleCalendar.java @@ -144,7 +144,8 @@ public class ScheduleCalendar { } return mSchedule.exitAtAlarm && mSchedule.nextAlarm != 0 - && time >= mSchedule.nextAlarm; + && time >= mSchedule.nextAlarm + && isInSchedule(mSchedule.nextAlarm); } private boolean isInSchedule(int daysOffset, long time, long start, long end) { diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 4431bcef1ff4..758cd2b877f2 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -62,6 +62,7 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -457,6 +458,7 @@ public class ApkSignatureSchemeV3Verifier { // get the version code, but don't do anything with it: creator knew about all our flags porBuf.getInt(); + HashSet<X509Certificate> certHistorySet = new HashSet<>(); while (porBuf.hasRemaining()) { levelCount++; ByteBuffer level = getLengthPrefixedSlice(porBuf); @@ -495,6 +497,12 @@ public class ApkSignatureSchemeV3Verifier { lastCert = new VerbatimX509Certificate(lastCert, encodedCert); lastSigAlgorithm = sigAlgorithm; + if (certHistorySet.contains(lastCert)) { + throw new SecurityException("Encountered duplicate entries in " + + "Proof-of-rotation record at certificate #" + levelCount + ". All " + + "signing certificates should be unique"); + } + certHistorySet.add(lastCert); certs.add(lastCert); flagsList.add(flags); } diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 3b8fc5c53d2a..f15e1a1a7c52 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -72,22 +72,31 @@ abstract class ApkVerityBuilder { signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; long dataSize = apk.length() - signingBlockSize; int[] levelOffset = calculateVerityLevelOffset(dataSize); + int merkleTreeSize = levelOffset[levelOffset.length - 1]; ByteBuffer output = bufferFactory.create( - CHUNK_SIZE_BYTES + // fsverity header + extensions + padding - levelOffset[levelOffset.length - 1]); // Merkle tree size + merkleTreeSize + + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata output.order(ByteOrder.LITTLE_ENDIAN); - ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES); - ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); - ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit()); + ByteBuffer tree = slice(output, 0, merkleTreeSize); + ByteBuffer header = slice(output, merkleTreeSize, + merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES); + ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES, + merkleTreeSize + CHUNK_SIZE_BYTES); byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES]; ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes); apkDigest.order(ByteOrder.LITTLE_ENDIAN); + // NB: Buffer limit is set inside once finished. calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions); - output.rewind(); + // Put the reverse offset to fs-verity header at the end. + output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit()); + output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit() + + 4); // size of this integer right before EOF + output.flip(); + return new ApkVerityResult(output, apkDigestBytes); } @@ -101,7 +110,8 @@ abstract class ApkVerityBuilder { ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES) .order(ByteOrder.LITTLE_ENDIAN); ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES); - ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); + ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, + CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES); calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions); @@ -328,10 +338,10 @@ abstract class ApkVerityBuilder { buffer.put((byte) 12); // log2(block-size): log2(4096) buffer.put((byte) 7); // log2(leaves-per-node): log2(4096 / 32) - buffer.putShort((short) 1); // meta algorithm, SHA256_MODE == 1 - buffer.putShort((short) 1); // data algorithm, SHA256_MODE == 1 + buffer.putShort((short) 1); // meta algorithm, SHA256 == 1 + buffer.putShort((short) 1); // data algorithm, SHA256 == 1 - buffer.putInt(0x0); // flags + buffer.putInt(0); // flags buffer.putInt(0); // reserved buffer.putLong(fileSize); // original file size @@ -362,12 +372,11 @@ abstract class ApkVerityBuilder { // // struct fsverity_extension_patch { // __le64 offset; - // u8 length; - // u8 reserved[7]; // u8 databytes[]; // }; final int kSizeOfFsverityExtensionHeader = 8; + final int kExtensionSizeAlignment = 8; { // struct fsverity_extension #1 @@ -385,24 +394,25 @@ abstract class ApkVerityBuilder { { // struct fsverity_extension #2 - final int kSizeOfFsverityPatchExtension = - 8 + // offset size - 1 + // size of length from offset (up to 255) - 7 + // reserved - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; - final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8); + final int kTotalSize = kSizeOfFsverityExtensionHeader + + 8 // offset size + + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; - buffer.putShort((short) // total size of extension, padded to 64-bit alignment - (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding)); + buffer.putShort((short) kTotalSize); buffer.put((byte) 1); // ID of patch extension skip(buffer, 5); // reserved // struct fsverity_extension_patch - buffer.putLong(eocdOffset); // offset - buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length - skip(buffer, 7); // reserved - buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes - skip(buffer, kPadding); // padding + buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET); // offset + buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes + + // The extension needs to be 0-padded at the end, since the length may not be multiple + // of 8. + int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment; + if (kPadding == kExtensionSizeAlignment) { + kPadding = 0; + } + skip(buffer, kPadding); // padding } buffer.flip(); diff --git a/core/java/android/util/apk/VerbatimX509Certificate.java b/core/java/android/util/apk/VerbatimX509Certificate.java index 9984c6d26c64..391c5fc39416 100644 --- a/core/java/android/util/apk/VerbatimX509Certificate.java +++ b/core/java/android/util/apk/VerbatimX509Certificate.java @@ -18,6 +18,7 @@ package android.util.apk; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; +import java.util.Arrays; /** * For legacy reasons we need to return exactly the original encoded certificate bytes, instead @@ -25,6 +26,7 @@ import java.security.cert.X509Certificate; */ class VerbatimX509Certificate extends WrappedX509Certificate { private final byte[] mEncodedVerbatim; + private int mHash = -1; VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) { super(wrapped); @@ -35,4 +37,30 @@ class VerbatimX509Certificate extends WrappedX509Certificate { public byte[] getEncoded() throws CertificateEncodingException { return mEncodedVerbatim; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VerbatimX509Certificate)) return false; + + try { + byte[] a = this.getEncoded(); + byte[] b = ((VerbatimX509Certificate) o).getEncoded(); + return Arrays.equals(a, b); + } catch (CertificateEncodingException e) { + return false; + } + } + + @Override + public int hashCode() { + if (mHash == -1) { + try { + mHash = Arrays.hashCode(this.getEncoded()); + } catch (CertificateEncodingException e) { + mHash = 0; + } + } + return mHash; + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f6c669b52370..ea3710c799bf 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13917,11 +13917,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mUnbufferedDispatchRequested = true; } + private boolean hasSize() { + return (mBottom > mTop) && (mRight > mLeft); + } + private boolean canTakeFocus() { return ((mViewFlags & VISIBILITY_MASK) == VISIBLE) && ((mViewFlags & FOCUSABLE) == FOCUSABLE) && ((mViewFlags & ENABLED_MASK) == ENABLED) - && (sCanFocusZeroSized || !isLayoutValid() || (mBottom > mTop) && (mRight > mLeft)); + && (sCanFocusZeroSized || !isLayoutValid() || hasSize()); } /** @@ -13982,7 +13986,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, || focusableChangedByAuto == 0 || viewRootImpl == null || viewRootImpl.mThread == Thread.currentThread()) { - shouldNotifyFocusableAvailable = true; + shouldNotifyFocusableAvailable = canTakeFocus(); } } } @@ -14001,11 +14005,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, needGlobalAttributesUpdate(true); - // a view becoming visible is worth notifying the parent - // about in case nothing has focus. even if this specific view - // isn't focusable, it may contain something that is, so let - // the root view try to give this focus if nothing else does. - shouldNotifyFocusableAvailable = true; + // a view becoming visible is worth notifying the parent about in case nothing has + // focus. Even if this specific view isn't focusable, it may contain something that + // is, so let the root view try to give this focus if nothing else does. + shouldNotifyFocusableAvailable = hasSize(); } } @@ -14014,16 +14017,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // a view becoming enabled should notify the parent as long as the view is also // visible and the parent wasn't already notified by becoming visible during this // setFlags invocation. - shouldNotifyFocusableAvailable = true; + shouldNotifyFocusableAvailable = canTakeFocus(); } else { if (isFocused()) clearFocus(); } } - if (shouldNotifyFocusableAvailable) { - if (mParent != null && canTakeFocus()) { - mParent.focusableViewAvailable(this); - } + if (shouldNotifyFocusableAvailable && mParent != null) { + mParent.focusableViewAvailable(this); } /* Check if the GONE bit has changed */ diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 59b14f18eb07..ae7ba1948c00 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -61,7 +61,6 @@ interface ILockSettings { void initRecoveryServiceWithSigFile(in String rootCertificateAlias, in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile); KeyChainSnapshot getKeyChainSnapshot(); - byte[] generateAndStoreKey(String alias); String generateKey(String alias); String importKey(String alias, in byte[] keyBytes); String getKey(String alias); diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 51dd92961f54..e8fc5989354a 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1000,6 +1000,11 @@ public class LockPatternView extends View { setPatternInProgress(false); cancelLineAnimations(); notifyPatternDetected(); + // Also clear pattern if fading is enabled + if (mFadePattern) { + clearPatternDrawLookup(); + mPatternDisplayMode = DisplayMode.Correct; + } invalidate(); } if (PROFILE_DRAWING) { diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index c9bfa137c7ac..7f90d8ee3e4e 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -448,8 +448,9 @@ static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle, jboolean hasAlpha, jlong paintHandle) { // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will // correct the alphaType to kOpaque_SkAlphaType. - SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, - GraphicsJNI::defaultColorSpace()); + SkImageInfo info = SkImageInfo::Make(width, height, + hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType, + kPremul_SkAlphaType); SkBitmap bitmap; bitmap.setInfo(info); sk_sp<Bitmap> androidBitmap = Bitmap::allocateHeapBitmap(&bitmap); diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 0fea0dc1c428..ea0b825b01a8 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -175,9 +175,9 @@ message IncidentProto { ]; optional GZippedFileProto last_kmsg = 2007 [ - (section).type = SECTION_NONE, // disable until selinux permission is gained + (section).type = SECTION_GZIP, (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg", - (privacy).dest = DEST_AUTOMATIC + (privacy).dest = DEST_EXPLICIT ]; // System Services diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 449d3e7beca1..c63f31997bcb 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -33,11 +33,16 @@ a light UI more visible. --> <drawable name="screen_background_light_transparent">#80ffffff</drawable> <color name="safe_mode_text">#80ffffff</color> + <!-- The color white, equivalent to 0xffffffff --> <color name="white">#ffffffff</color> + <!-- The color black, equivalent to 0xff000000 --> <color name="black">#ff000000</color> <color name="red">#ffff0000</color> + <!-- Fully transparent, equivalent to 0x00000000 --> <color name="transparent">#00000000</color> + <!-- Equivalent to 0xff000000 --> <color name="background_dark">#ff000000</color> + <!-- Equivalent to 0xffffffff --> <color name="background_light">#ffffffff</color> <color name="bright_foreground_dark">@android:color/background_light</color> <color name="bright_foreground_light">@android:color/background_dark</color> @@ -80,6 +85,7 @@ <!-- For settings framework --> <color name="lighter_gray">#ddd</color> + <!-- A dark gray, equivalent to 0xffaaaaaa --> <color name="darker_gray">#aaa</color> <!-- For security permissions --> diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml index 917c781812ca..4297eeaafdc4 100644 --- a/core/res/res/values/colors_holo.xml +++ b/core/res/res/values/colors_holo.xml @@ -50,29 +50,29 @@ <!-- General purpose colors for Holo-themed elements --> <eat-comment /> - <!-- A light Holo shade of blue --> + <!-- A light Holo shade of blue. Equivalent to #ff33b5e5. --> <color name="holo_blue_light">#ff33b5e5</color> - <!-- A light Holo shade of gray --> + <!-- A light Holo shade of gray. Equivalent to #33999999. --> <color name="holo_gray_light">#33999999</color> - <!-- A light Holo shade of green --> + <!-- A light Holo shade of green. Equivalent to #ff99cc00. --> <color name="holo_green_light">#ff99cc00</color> - <!-- A light Holo shade of red --> + <!-- A light Holo shade of red. Equivalent to #ffff4444. <--> <color name="holo_red_light">#ffff4444</color> - <!-- A dark Holo shade of blue --> + <!-- A dark Holo shade of blue. Equivalent to #ff0099cc --> <color name="holo_blue_dark">#ff0099cc</color> - <!-- A dark Holo shade of green --> + <!-- A dark Holo shade of green. Equivalent to #ff669900 --> <color name="holo_green_dark">#ff669900</color> - <!-- A dark Holo shade of red --> + <!-- A dark Holo shade of red. Equivalent to #ffcc0000 --> <color name="holo_red_dark">#ffcc0000</color> - <!-- A Holo shade of purple --> + <!-- A Holo shade of purple. Equivalent to #ffaa66cc --> <color name="holo_purple">#ffaa66cc</color> - <!-- A light Holo shade of orange --> + <!-- A light Holo shade of orange. Equivalent to #ffffbb33. --> <color name="holo_orange_light">#ffffbb33</color> - <!-- A dark Holo shade of orange --> + <!-- A dark Holo shade of orange. Equivalent to ffff8800. --> <color name="holo_orange_dark">#ffff8800</color> - <!-- A really bright Holo shade of blue --> + <!-- A really bright Holo shade of blue. Equivalent to #ff00ddff. --> <color name="holo_blue_bright">#ff00ddff</color> - <!-- A really bright Holo shade of gray --> + <!-- A really bright Holo shade of gray. Equivalent to #33cccccc. --> <color name="holo_gray_bright">#33CCCCCC</color> <!-- Forward compatibility for Material-style theme colors --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 66d25df41e63..2a6b33108881 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -30,33 +30,32 @@ <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item> <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item> <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_location</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item> <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item> <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item> <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item> <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_location</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item> <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item> </string-array> <string translatable="false" name="status_bar_rotate">rotate</string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 0246c804952c..42cc54f83f46 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -708,23 +708,38 @@ <public type="dimen" name="thumbnail_height" id="0x01050001" /> <public type="dimen" name="thumbnail_width" id="0x01050002" /> + <!-- Equivalent to 0xffaaaaaa --> <public type="color" name="darker_gray" id="0x01060000" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="primary_text_dark" id="0x01060001" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="primary_text_dark_nodisable" id="0x01060002" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="primary_text_light" id="0x01060003" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="primary_text_light_nodisable" id="0x01060004" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="secondary_text_dark" id="0x01060005" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="secondary_text_dark_nodisable" id="0x01060006" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="secondary_text_light" id="0x01060007" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="secondary_text_light_nodisable" id="0x01060008" /> + <!-- Equivalent to 0xff808080 --> <public type="color" name="tab_indicator_text" id="0x01060009" /> + <!-- Equivalent to 0xff000000 --> <public type="color" name="widget_edittext_dark" id="0x0106000a" /> <public type="color" name="white" id="0x0106000b" /> <public type="color" name="black" id="0x0106000c" /> <public type="color" name="transparent" id="0x0106000d" /> + <!-- Equivalent to 0xff000000 --> <public type="color" name="background_dark" id="0x0106000e" /> + <!-- Equivalent to 0xffffffff --> <public type="color" name="background_light" id="0x0106000f" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="tertiary_text_dark" id="0x01060010" /> + <!-- {@deprecated Use a text color from your theme instead.} --> <public type="color" name="tertiary_text_light" id="0x01060011" /> <public type="array" name="emailAddressTypes" id="0x01070000" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cb8d629abaaf..cbd7b4ddb2bc 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2732,6 +2732,10 @@ <java-symbol type="string" name="status_bar_alarm_clock" /> <java-symbol type="string" name="status_bar_secure" /> <java-symbol type="string" name="status_bar_clock" /> + <java-symbol type="string" name="status_bar_airplane" /> + <java-symbol type="string" name="status_bar_mobile" /> + <java-symbol type="string" name="status_bar_ethernet" /> + <java-symbol type="string" name="status_bar_vpn" /> <!-- Locale picker --> <java-symbol type="id" name="locale_search_menu" /> diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java index 82af662e207e..20b298d639d2 100644 --- a/core/tests/coretests/src/android/os/MemoryFileTest.java +++ b/core/tests/coretests/src/android/os/MemoryFileTest.java @@ -23,6 +23,7 @@ import android.test.suitebuilder.annotation.SmallTest; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.BufferOverflowException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -110,7 +111,7 @@ public class MemoryFileTest extends AndroidTestCase { try { os.write(new byte[] { -1, -1 }); fail(); - } catch (IndexOutOfBoundsException expected) { + } catch (IndexOutOfBoundsException | BufferOverflowException expected) { } byte[] copy = new byte[file.length()]; diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 4b9465d24fc9..dfc99f6fcb3a 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -267,6 +267,7 @@ public class SettingsBackupTest { Settings.Global.LOW_POWER_MODE, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, Settings.Global.LOW_POWER_MODE_STICKY, + Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS, Settings.Global.LTE_SERVICE_FORCED, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 5261c0486acf..00dc22ea162b 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -855,7 +855,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setResize(int width, int height) { - return this.setTargetSize(width, height); + this.setTargetSize(width, height); + return this; } /** @@ -873,9 +874,8 @@ public final class ImageDecoder implements AutoCloseable { * * @param width must be greater than 0. * @param height must be greater than 0. - * @return this object for chaining. */ - public ImageDecoder setTargetSize(int width, int height) { + public void setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("Dimensions must be positive! " + "provided (" + width + ", " + height + ")"); @@ -883,7 +883,6 @@ public final class ImageDecoder implements AutoCloseable { mDesiredWidth = width; mDesiredHeight = height; - return this; } /** @removed @@ -891,7 +890,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setResize(int sampleSize) { - return this.setTargetSampleSize(sampleSize); + this.setTargetSampleSize(sampleSize); + return this; } private int getTargetDimension(int original, int sampleSize, int computed) { @@ -941,13 +941,12 @@ public final class ImageDecoder implements AutoCloseable { * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * * @param sampleSize Sampling rate of the encoded image. - * @return this object for chaining. */ - public ImageDecoder setTargetSampleSize(int sampleSize) { + public void setTargetSampleSize(int sampleSize) { Size size = this.getSampledSize(sampleSize); int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth()); int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight()); - return this.setTargetSize(targetWidth, targetHeight); + this.setTargetSize(targetWidth, targetHeight); } private boolean requestedResize() { @@ -1006,14 +1005,12 @@ public final class ImageDecoder implements AutoCloseable { * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * * @param allocator Type of allocator to use. - * @return this object for chaining. */ - public ImageDecoder setAllocator(@Allocator int allocator) { + public void setAllocator(@Allocator int allocator) { if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) { throw new IllegalArgumentException("invalid allocator " + allocator); } mAllocator = allocator; - return this; } /** @@ -1039,12 +1036,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setUnpremultipliedRequired(boolean unpremultipliedRequired) { + public void setUnpremultipliedRequired(boolean unpremultipliedRequired) { mUnpremultipliedRequired = unpremultipliedRequired; - return this; } /** @removed @@ -1052,7 +1046,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) { - return this.setUnpremultipliedRequired(unpremultipliedRequired); + this.setUnpremultipliedRequired(unpremultipliedRequired); + return this; } /** @@ -1086,11 +1081,9 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. */ - public ImageDecoder setPostProcessor(@Nullable PostProcessor p) { + public void setPostProcessor(@Nullable PostProcessor p) { mPostProcessor = p; - return this; } /** @@ -1110,11 +1103,9 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. */ - public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) { + public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { mOnPartialImageListener = l; - return this; } /** @@ -1140,11 +1131,9 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. */ - public ImageDecoder setCrop(@Nullable Rect subset) { + public void setCrop(@Nullable Rect subset) { mCropRect = subset; - return this; } /** @@ -1164,13 +1153,10 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. - * * @hide */ - public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) { + public void setOutPaddingRect(@NonNull Rect outPadding) { mOutPaddingRect = outPadding; - return this; } /** @@ -1191,12 +1177,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setMutableRequired(boolean mutable) { + public void setMutableRequired(boolean mutable) { mMutable = mutable; - return this; } /** @removed @@ -1204,7 +1187,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setMutable(boolean mutable) { - return this.setMutableRequired(mutable); + this.setMutableRequired(mutable); + return this; } /** @@ -1236,12 +1220,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setConserveMemory(boolean conserveMemory) { + public void setConserveMemory(boolean conserveMemory) { mConserveMemory = conserveMemory; - return this; } /** @@ -1269,12 +1250,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setDecodeAsAlphaMaskEnabled(boolean enabled) { + public void setDecodeAsAlphaMaskEnabled(boolean enabled) { mDecodeAsAlphaMask = enabled; - return this; } /** @removed @@ -1282,7 +1260,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setDecodeAsAlphaMask(boolean enabled) { - return this.setDecodeAsAlphaMaskEnabled(enabled); + this.setDecodeAsAlphaMaskEnabled(enabled); + return this; } /** @removed @@ -1290,7 +1269,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setAsAlphaMask(boolean asAlphaMask) { - return this.setDecodeAsAlphaMask(asAlphaMask); + this.setDecodeAsAlphaMask(asAlphaMask); + return this; } /** @@ -1350,9 +1330,8 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> */ - public ImageDecoder setTargetColorSpace(ColorSpace colorSpace) { + public void setTargetColorSpace(ColorSpace colorSpace) { mDesiredColorSpace = colorSpace; - return this; } @Override diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 1e2ebf894f99..fe05c13c999b 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -16,6 +16,7 @@ package android.security; +import android.app.ActivityManager; import android.app.ActivityThread; import android.app.Application; import android.app.KeyguardManager; @@ -279,7 +280,7 @@ public class KeyStore { /** * Attempt to lock the keystore for {@code user}. * - * @param user Android user to lock. + * @param userId Android user to lock. * @return whether {@code user}'s keystore was locked. */ public boolean lock(int userId) { @@ -300,7 +301,7 @@ public class KeyStore { * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or * created. * - * @param user Android user ID to operate on + * @param userId Android user ID to operate on * @param password user's keystore password. Should be the most recent value passed to * {@link #onUserPasswordChanged} for the user. * diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 4b9f3c803481..c342acdf101e 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -266,6 +266,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mInvalidatedByBiometricEnrollment; private final boolean mIsStrongBoxBacked; private final boolean mUserConfirmationRequired; + private final boolean mUnlockedDeviceRequired; /** * @hide should be built with Builder @@ -296,7 +297,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, boolean isStrongBoxBacked, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -345,6 +347,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mIsStrongBoxBacked = isStrongBoxBacked; mUserConfirmationRequired = userConfirmationRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -670,6 +673,15 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * 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() { @@ -707,6 +719,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mInvalidatedByBiometricEnrollment = true; private boolean mIsStrongBoxBacked = false; private boolean mUserConfirmationRequired; + private boolean mUnlockedDeviceRequired = false; /** * Creates a new instance of the {@code Builder}. @@ -1275,6 +1288,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * 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 @@ -1305,7 +1330,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 95eeec704847..22568ce7a596 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -224,12 +224,13 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final boolean mRandomizedEncryptionRequired; private final boolean mUserAuthenticationRequired; private final int mUserAuthenticationValidityDurationSeconds; - private final boolean mTrustedUserPresenceRequred; + private final boolean mTrustedUserPresenceRequired; private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mInvalidatedByBiometricEnrollment; private final long mBoundToSecureUserId; private final boolean mCriticalToDeviceEncryption; private final boolean mUserConfirmationRequired; + private final boolean mUnlockedDeviceRequired; private KeyProtection( Date keyValidityStart, @@ -243,12 +244,13 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, - boolean trustedUserPresenceRequred, + 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); @@ -262,12 +264,13 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticationRequired = userAuthenticationRequired; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; - mTrustedUserPresenceRequred = trustedUserPresenceRequred; + mTrustedUserPresenceRequired = trustedUserPresenceRequired; mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mBoundToSecureUserId = boundToSecureUserId; mCriticalToDeviceEncryption = criticalToDeviceEncryption; mUserConfirmationRequired = userConfirmationRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -444,7 +447,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls. */ public boolean isTrustedUserPresenceRequired() { - return mTrustedUserPresenceRequred; + return mTrustedUserPresenceRequired; } /** @@ -505,6 +508,15 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#setUnlockedDeviceRequired(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** * Builder of {@link KeyProtection} instances. */ public final static class Builder { @@ -524,6 +536,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; private boolean mUserConfirmationRequired; + private boolean mUnlockedDeviceRequired = false; + private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; @@ -914,6 +928,18 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * 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 @@ -937,7 +963,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { 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 0ef08f237b4e..14a9970c66d4 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -16,9 +16,8 @@ package android.security.keystore; -import android.util.Log; +import android.app.ActivityManager; import android.hardware.fingerprint.FingerprintManager; -import android.os.UserHandle; import android.security.GateKeeper; import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; @@ -101,8 +100,8 @@ public abstract class KeymasterUtils { * state (e.g., secure lock screen not set up) for generating or importing keys that * require user authentication. */ - public static void addUserAuthArgs(KeymasterArguments args, - UserAuthArgs spec) { + public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) { + if (spec.isUserConfirmationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); } @@ -111,6 +110,10 @@ public abstract class KeymasterUtils { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); } + if (spec.isUnlockedDeviceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED); + } + if (!spec.isUserAuthenticationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); return; diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java index 1949592e7240..ad18ff8aef76 100644 --- a/keystore/java/android/security/keystore/UserAuthArgs.java +++ b/keystore/java/android/security/keystore/UserAuthArgs.java @@ -33,5 +33,6 @@ public interface UserAuthArgs { boolean isUserConfirmationRequired(); long getBoundToSpecificSecureUserId(); boolean isTrustedUserPresenceRequired(); + boolean isUnlockedDeviceRequired(); } diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index 2fa56f613144..670074871c71 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -83,10 +83,12 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { int maxTries = 3; while (valueSize > mObservedBlobValueSize && maxTries > 0) { mObservedBlobValueSize = std::min(valueSize, maxValueSize); - valueBuffer = realloc(valueBuffer, mObservedBlobValueSize); - if (!valueBuffer) { + void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize); + if (!newValueBuffer) { + free(valueBuffer); return nullptr; } + valueBuffer = newValueBuffer; valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); maxTries--; } diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 86dfc9c88723..ca895fcdfc4a 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -226,11 +226,10 @@ public final class AudioDeviceInfo { } /** - * @hide * @return The "address" string of the device. This generally contains device-specific * parameters. */ - public String getAddress() { + public @NonNull String getAddress() { return mPort.address(); } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e408a115787c..aeef2158b20f 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4596,8 +4596,7 @@ public class AudioManager { private static boolean checkTypes(AudioDevicePort port) { return AudioDeviceInfo.convertInternalDeviceToDeviceType(port.type()) != - AudioDeviceInfo.TYPE_UNKNOWN && - port.type() != AudioSystem.DEVICE_IN_BACK_MIC; + AudioDeviceInfo.TYPE_UNKNOWN; } /** diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 8d62cac133c3..51737437d692 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -705,7 +705,9 @@ public final class MediaDrm implements AutoCloseable { public @interface KeyType {} /** - * Contains the opaque data an app uses to request keys from a license server + * Contains the opaque data an app uses to request keys from a license server. + * These request types may or may not be generated by a given plugin. Refer + * to plugin vendor documentation for more information. */ public static final class KeyRequest { private byte[] mData; @@ -730,8 +732,8 @@ public final class MediaDrm implements AutoCloseable { public static final int REQUEST_TYPE_RELEASE = 2; /** - * Keys are already loaded. No license request is necessary, and no - * key request data is returned. + * Keys are already loaded and are available for use. No license request is necessary, and + * no key request data is returned. */ public static final int REQUEST_TYPE_NONE = 3; diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java index 004efea64d2d..d6399a4163fe 100644 --- a/media/java/android/media/MicrophoneInfo.java +++ b/media/java/android/media/MicrophoneInfo.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.NonNull; import android.util.Pair; import java.lang.annotation.Retention; @@ -224,12 +225,11 @@ public final class MicrophoneInfo { } /** - * @hide * Returns The "address" string of the microphone that corresponds to the * address returned by {@link AudioDeviceInfo#getAddress()} * @return the address of the microphone */ - public String getAddress() { + public @NonNull String getAddress() { return mAddress; } diff --git a/media/java/android/media/audiofx/DynamicsProcessing.java b/media/java/android/media/audiofx/DynamicsProcessing.java index d09c9a895e0c..4c17ae1d93b3 100644 --- a/media/java/android/media/audiofx/DynamicsProcessing.java +++ b/media/java/android/media/audiofx/DynamicsProcessing.java @@ -16,10 +16,11 @@ package android.media.audiofx; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.media.AudioTrack; import android.media.MediaPlayer; import android.media.audiofx.AudioEffect; -import android.media.audiofx.DynamicsProcessing.Settings; import android.util.Log; import java.nio.ByteBuffer; @@ -90,66 +91,18 @@ public final class DynamicsProcessing extends AudioEffect { private final static String TAG = "DynamicsProcessing"; - /** - * Config object used to initialize and change effect parameters at runtime. - */ - private Config mConfig = null; - - // These parameter constants must be synchronized with those in // /system/media/audio_effects/include/audio_effects/effect_dynamicsprocessing.h - - private static final int PARAM_GET_CHANNEL_COUNT = 0x0; - private static final int PARAM_EQ_BAND_COUNT = 0x1; - private static final int PARAM_MBC_BAND_COUNT = 0x2; - private static final int PARAM_INPUT_GAIN = 0x3; - private static final int PARAM_PRE_EQ_ENABLED = 0x10; - private static final int PARAM_PRE_EQ_BAND_ENABLED = 0x11; - private static final int PARAM_PRE_EQ_BAND_FREQUENCY = 0x12; - private static final int PARAM_PRE_EQ_BAND_GAIN = 0x13; - private static final int PARAM_EQ_FREQUENCY_RANGE = 0x22; - private static final int PARAM_EQ_GAIN_RANGE = 0x23; - private static final int PARAM_MBC_ENABLED = 0x30; - private static final int PARAM_MBC_BAND_ENABLED = 0x31; - private static final int PARAM_MBC_BAND_FREQUENCY = 0x32; - private static final int PARAM_MBC_BAND_ATTACK_TIME = 0x33; - private static final int PARAM_MBC_BAND_RELEASE_TIME = 0x34; - private static final int PARAM_MBC_BAND_RATIO = 0x35; - private static final int PARAM_MBC_BAND_THRESHOLD = 0x36; - private static final int PARAM_MBC_BAND_KNEE_WIDTH = 0x37; - private static final int PARAM_MBC_BAND_NOISE_GATE_THRESHOLD = 0x38; - private static final int PARAM_MBC_BAND_EXPANDER_RATIO = 0x39; - private static final int PARAM_MBC_BAND_GAIN_PRE = 0x3A; - private static final int PARAM_MBC_BAND_GAIN_POST = 0x3B; - private static final int PARAM_MBC_FREQUENCY_RANGE = 0x42; - private static final int PARAM_MBC_ATTACK_TIME_RANGE = 0x43; - private static final int PARAM_MBC_RELEASE_TIME_RANGE = 0x44; - private static final int PARAM_MBC_RATIO_RANGE = 0x45; - private static final int PARAM_MBC_THRESHOLD_RANGE = 0x46; - private static final int PARAM_MBC_KNEE_WIDTH_RANGE = 0x47; - private static final int PARAM_MBC_NOISE_GATE_THRESHOLD_RANGE = 0x48; - private static final int PARAM_MBC_EXPANDER_RATIO_RANGE = 0x49; - private static final int PARAM_MBC_GAIN_RANGE = 0x4A; - private static final int PARAM_POST_EQ_ENABLED = 0x50; - private static final int PARAM_POST_EQ_BAND_ENABLED = 0x51; - private static final int PARAM_POST_EQ_BAND_FREQUENCY = 0x52; - private static final int PARAM_POST_EQ_BAND_GAIN = 0x53; - private static final int PARAM_LIMITER_ENABLED = 0x60; - private static final int PARAM_LIMITER_LINK_GROUP = 0x61; - private static final int PARAM_LIMITER_ATTACK_TIME = 0x62; - private static final int PARAM_LIMITER_RELEASE_TIME = 0x63; - private static final int PARAM_LIMITER_RATIO = 0x64; - private static final int PARAM_LIMITER_THRESHOLD = 0x65; - private static final int PARAM_LIMITER_GAIN_POST = 0x66; - private static final int PARAM_LIMITER_ATTACK_TIME_RANGE = 0x72; - private static final int PARAM_LIMITER_RELEASE_TIME_RANGE = 0x73; - private static final int PARAM_LIMITER_RATIO_RANGE = 0x74; - private static final int PARAM_LIMITER_THRESHOLD_RANGE = 0x75; - private static final int PARAM_LIMITER_GAIN_RANGE = 0x76; - private static final int PARAM_VARIANT = 0x100; - private static final int PARAM_VARIANT_DESCRIPTION = 0x101; - private static final int PARAM_VARIANT_COUNT = 0x102; - private static final int PARAM_SET_ENGINE_ARCHITECTURE = 0x200; + private static final int PARAM_GET_CHANNEL_COUNT = 0x10; + private static final int PARAM_INPUT_GAIN = 0x20; + private static final int PARAM_ENGINE_ARCHITECTURE = 0x30; + private static final int PARAM_PRE_EQ = 0x40; + private static final int PARAM_PRE_EQ_BAND = 0x45; + private static final int PARAM_MBC = 0x50; + private static final int PARAM_MBC_BAND = 0x55; + private static final int PARAM_POST_EQ = 0x60; + private static final int PARAM_POST_EQ_BAND = 0x65; + private static final int PARAM_LIMITER = 0x70; /** * Index of variant that favors frequency resolution. Frequency domain based implementation. @@ -226,12 +179,13 @@ public final class DynamicsProcessing extends AudioEffect { * Config object that suits your needs. A null cfg parameter will create and use a default * configuration for the effect */ - public DynamicsProcessing(int priority, int audioSession, Config cfg) { + public DynamicsProcessing(int priority, int audioSession, @Nullable Config cfg) { super(EFFECT_TYPE_DYNAMICS_PROCESSING, EFFECT_TYPE_NULL, priority, audioSession); if (audioSession == 0) { Log.w(TAG, "WARNING: attaching a DynamicsProcessing to global output mix is" + "deprecated!"); } + final Config config; mChannelCount = getChannelCount(); if (cfg == null) { //create a default configuration and effect, with the number of channels this effect has @@ -239,21 +193,33 @@ public final class DynamicsProcessing extends AudioEffect { new DynamicsProcessing.Config.Builder( CONFIG_DEFAULT_VARIANT, mChannelCount, - true /*use preEQ*/, 6 /*pre eq bands*/, - true /*use mbc*/, 6 /*mbc bands*/, - true /*use postEQ*/, 6 /*postEq bands*/, - true /*use Limiter*/); - mConfig = builder.build(); + CONFIG_DEFAULT_USE_PREEQ, + CONFIG_DEFAULT_PREEQ_BANDS, + CONFIG_DEFAULT_USE_MBC, + CONFIG_DEFAULT_MBC_BANDS, + CONFIG_DEFAULT_USE_POSTEQ, + CONFIG_DEFAULT_POSTEQ_BANDS, + CONFIG_DEFAULT_USE_LIMITER); + config = builder.build(); } else { - //validate channels are ok. decide what to do: replicate channels if more, or fail, or - mConfig = new DynamicsProcessing.Config(mChannelCount, cfg); + //validate channels are ok. decide what to do: replicate channels if more + config = new DynamicsProcessing.Config(mChannelCount, cfg); + } + + //configure engine + setEngineArchitecture(config.getVariant(), + config.getPreferredFrameDuration(), + config.isPreEqInUse(), + config.getPreEqBandCount(), + config.isMbcInUse(), + config.getMbcBandCount(), + config.isPostEqInUse(), + config.getPostEqBandCount(), + config.isLimiterInUse()); + //update all the parameters + for (int ch = 0; ch < mChannelCount; ch++) { + updateEngineChannelByChannelIndex(ch, config.getChannelByChannelIndex(ch)); } - - setEngineArchitecture(mConfig.getVariant(), - mConfig.isPreEqInUse(), mConfig.getPreEqBandCount(), - mConfig.isMbcInUse(), mConfig.getMbcBandCount(), - mConfig.isPostEqInUse(), mConfig.getPostEqBandCount(), - mConfig.isLimiterInUse()); } /** @@ -261,11 +227,51 @@ public final class DynamicsProcessing extends AudioEffect { * @return Config Current Config object used to setup this DynamicsProcessing effect. */ public Config getConfig() { - return mConfig; + //Query engine architecture to create config object + Number[] params = { PARAM_ENGINE_ARCHITECTURE }; + Number[] values = { 0 /*0 variant */, + 0.0f /* 1 preferredFrameDuration */, + 0 /*2 preEqInUse */, + 0 /*3 preEqBandCount */, + 0 /*4 mbcInUse */, + 0 /*5 mbcBandCount*/, + 0 /*6 postEqInUse */, + 0 /*7 postEqBandCount */, + 0 /*8 limiterInUse */}; + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. + getParameter(paramBytes, valueBytes); + byteArrayToNumberArray(valueBytes, values); + DynamicsProcessing.Config.Builder builder = + new DynamicsProcessing.Config.Builder( + values[0].intValue(), + mChannelCount, + values[2].intValue() > 0 /*use preEQ*/, + values[3].intValue() /*pre eq bands*/, + values[4].intValue() > 0 /*use mbc*/, + values[5].intValue() /*mbc bands*/, + values[6].intValue() > 0 /*use postEQ*/, + values[7].intValue()/*postEq bands*/, + values[8].intValue() > 0 /*use Limiter*/). + setPreferredFrameDuration(values[1].floatValue()); + Config config = builder.build(); + for (int ch = 0; ch < mChannelCount; ch++) { + Channel channel = queryEngineByChannelIndex(ch); + config.setChannelTo(ch, channel); + } + return config; } - private static final int CONFIG_DEFAULT_VARIANT = 0; //favor frequency + private static final int CONFIG_DEFAULT_VARIANT = VARIANT_FAVOR_FREQUENCY_RESOLUTION; + private static final boolean CONFIG_DEFAULT_USE_PREEQ = true; + private static final int CONFIG_DEFAULT_PREEQ_BANDS = 6; + private static final boolean CONFIG_DEFAULT_USE_MBC = true; + private static final int CONFIG_DEFAULT_MBC_BANDS = 6; + private static final boolean CONFIG_DEFAULT_USE_POSTEQ = true; + private static final int CONFIG_DEFAULT_POSTEQ_BANDS = 6; + private static final boolean CONFIG_DEFAULT_USE_LIMITER = true; + private static final float CHANNEL_DEFAULT_INPUT_GAIN = 0; // dB private static final float CONFIG_PREFERRED_FRAME_DURATION_MS = 10.0f; //milliseconds @@ -1276,7 +1282,7 @@ public final class DynamicsProcessing extends AudioEffect { mChannel = new Channel[mChannelCount]; //check if channelconfig is null or has less channels than channel count. //options: fill the missing with default options. - // or fail? + // or fail? for (int ch = 0; ch < mChannelCount; ch++) { if (ch < channel.length) { mChannel[ch] = new Channel(channel[ch]); //copy create @@ -1331,7 +1337,7 @@ public final class DynamicsProcessing extends AudioEffect { * Class constructor for Config * @param cfg Configuration object copy constructor */ - public Config(Config cfg) { + public Config(@NonNull Config cfg) { this(cfg.mChannelCount, cfg); } @@ -1763,138 +1769,115 @@ public final class DynamicsProcessing extends AudioEffect { } //=== CHANNEL public Channel getChannelByChannelIndex(int channelIndex) { - return mConfig.getChannelByChannelIndex(channelIndex); + return queryEngineByChannelIndex(channelIndex); } public void setChannelTo(int channelIndex, Channel channel) { - mConfig.setChannelTo(channelIndex, channel); + updateEngineChannelByChannelIndex(channelIndex, channel); } public void setAllChannelsTo(Channel channel) { - mConfig.setAllChannelsTo(channel); + for (int ch = 0; ch < mChannelCount; ch++) { + setChannelTo(ch, channel); + } } //=== channel params public float getInputGainByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getInputGainByChannelIndex(channelIndex); + return getTwoFloat(PARAM_INPUT_GAIN, channelIndex); } public void setInputGainbyChannel(int channelIndex, float inputGain) { - mConfig.setInputGainByChannelIndex(channelIndex, inputGain); - //TODO: communicate change to engine + setTwoFloat(PARAM_INPUT_GAIN, channelIndex, inputGain); } public void setInputGainAllChannelsTo(float inputGain) { - mConfig.setInputGainAllChannelsTo(inputGain); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setInputGainbyChannel(ch, inputGain); + } } //=== PreEQ public Eq getPreEqByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getPreEqByChannelIndex(channelIndex); + return queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex); } - public void setPreEqByChannelIndex(int channelIndex, Eq preEq) { - mConfig.setPreEqByChannelIndex(channelIndex, preEq); - //TODO: communicate change to engine + updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq); } - public void setPreEqAllChannelsTo(Eq preEq) { - mConfig.setPreEqAllChannelsTo(preEq); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPreEqByChannelIndex(ch, preEq); + } } - public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) { - //TODO: return info from engine instead of cached config - return mConfig.getPreEqBandByChannelIndex(channelIndex, band); + return queryEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band); } - public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) { - mConfig.setPreEqBandByChannelIndex(channelIndex, band, preEqBand); - //TODO: communicate change to engine + updateEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band, preEqBand); } - public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) { - mConfig.setPreEqBandAllChannelsTo(band, preEqBand); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPreEqBandByChannelIndex(ch, band, preEqBand); + } } //=== MBC public Mbc getMbcByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getMbcByChannelIndex(channelIndex); + return queryEngineMbcByChannelIndex(channelIndex); } - public void setMbcByChannelIndex(int channelIndex, Mbc mbc) { - mConfig.setMbcByChannelIndex(channelIndex, mbc); - //TODO: communicate change to engine + updateEngineMbcByChannelIndex(channelIndex, mbc); } - public void setMbcAllChannelsTo(Mbc mbc) { - mConfig.setMbcAllChannelsTo(mbc); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setMbcByChannelIndex(ch, mbc); + } } - public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) { - //TODO: return info from engine instead of cached config - return mConfig.getMbcBandByChannelIndex(channelIndex, band); + return queryEngineMbcBandByChannelIndex(channelIndex, band); } - public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) { - mConfig.setMbcBandByChannelIndex(channelIndex, band, mbcBand); - //TODO: communicate change to engine + updateEngineMbcBandByChannelIndex(channelIndex, band, mbcBand); } - public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) { - mConfig.setMbcBandAllChannelsTo(band, mbcBand); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setMbcBandByChannelIndex(ch, band, mbcBand); + } } //== PostEq public Eq getPostEqByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getPostEqByChannelIndex(channelIndex); + return queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex); } - public void setPostEqByChannelIndex(int channelIndex, Eq postEq) { - mConfig.setPostEqByChannelIndex(channelIndex, postEq); - //TODO: communicate change to engine + updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq); } - public void setPostEqAllChannelsTo(Eq postEq) { - mConfig.setPostEqAllChannelsTo(postEq); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPostEqByChannelIndex(ch, postEq); + } } - public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) { - //TODO: return info from engine instead of cached config - return mConfig.getPostEqBandByChannelIndex(channelIndex, band); + return queryEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band); } - public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) { - mConfig.setPostEqBandByChannelIndex(channelIndex, band, postEqBand); - //TODO: communicate change to engine + updateEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band, postEqBand); } - public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) { - mConfig.setPostEqBandAllChannelsTo(band, postEqBand); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPostEqBandByChannelIndex(ch, band, postEqBand); + } } //==== Limiter public Limiter getLimiterByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getLimiterByChannelIndex(channelIndex); + return queryEngineLimiterByChannelIndex(channelIndex); } - public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) { - mConfig.setLimiterByChannelIndex(channelIndex, limiter); - //TODO: communicate change to engine + updateEngineLimiterByChannelIndex(channelIndex, limiter); } - public void setLimiterAllChannelsTo(Limiter limiter) { - mConfig.setLimiterAllChannelsTo(limiter); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setLimiterByChannelIndex(ch, limiter); + } } /** @@ -1905,165 +1888,327 @@ public final class DynamicsProcessing extends AudioEffect { return getOneInt(PARAM_GET_CHANNEL_COUNT); } - private void setEngineArchitecture(int variant, boolean preEqInUse, int preEqBandCount, - boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount, - boolean limiterInUse) { - int[] values = { variant, (preEqInUse ? 1 : 0), preEqBandCount, - (mbcInUse ? 1 : 0), mbcBandCount, (postEqInUse ? 1 : 0), postEqBandCount, + //=== Engine calls + private void setEngineArchitecture(int variant, float preferredFrameDuration, + boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount, + boolean postEqInUse, int postEqBandCount, boolean limiterInUse) { + + Number[] params = { PARAM_ENGINE_ARCHITECTURE }; + Number[] values = { variant /* variant */, + preferredFrameDuration, + (preEqInUse ? 1 : 0), + preEqBandCount, + (mbcInUse ? 1 : 0), + mbcBandCount, + (postEqInUse ? 1 : 0), + postEqBandCount, (limiterInUse ? 1 : 0)}; - //TODO: enable later setIntArray(PARAM_SET_ENGINE_ARCHITECTURE, values); + setNumberArray(params, values); + } + + private void updateEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex, + @NonNull EqBand eqBand) { + Number[] params = {param, + channelIndex, + bandIndex}; + Number[] values = {(eqBand.isEnabled() ? 1 : 0), + eqBand.getCutoffFrequency(), + eqBand.getGain()}; + setNumberArray(params, values); + } + private Eq queryEngineEqByChannelIndex(int param, int channelIndex) { + + Number[] params = {param == PARAM_PRE_EQ ? PARAM_PRE_EQ : PARAM_POST_EQ, + channelIndex}; + Number[] values = {0 /*0 in use */, + 0 /*1 enabled*/, + 0 /*2 band count */}; + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. + getParameter(paramBytes, valueBytes); + byteArrayToNumberArray(valueBytes, values); + int bandCount = values[2].intValue(); + Eq eq = new Eq(values[0].intValue() > 0 /* in use */, + values[1].intValue() > 0 /* enabled */, + bandCount /*band count*/); + for (int b = 0; b < bandCount; b++) { + EqBand eqBand = queryEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ? + PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b); + eq.setBand(b, eqBand); + } + return eq; + } + private EqBand queryEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex) { + Number[] params = {param, + channelIndex, + bandIndex}; + Number[] values = {0 /*0 enabled*/, + 0.0f /*1 cutoffFrequency */, + 0.0f /*2 gain */}; + + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. + getParameter(paramBytes, valueBytes); + + byteArrayToNumberArray(valueBytes, values); + + return new EqBand(values[0].intValue() > 0 /* enabled */, + values[1].floatValue() /* cutoffFrequency */, + values[2].floatValue() /* gain*/); + } + private void updateEngineEqByChannelIndex(int param, int channelIndex, @NonNull Eq eq) { + int bandCount = eq.getBandCount(); + Number[] params = {param, + channelIndex}; + Number[] values = { (eq.isInUse() ? 1 : 0), + (eq.isEnabled() ? 1 : 0), + bandCount}; + setNumberArray(params, values); + for (int b = 0; b < bandCount; b++) { + EqBand eqBand = eq.getBand(b); + updateEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ? + PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b, eqBand); + } + } + + private Mbc queryEngineMbcByChannelIndex(int channelIndex) { + Number[] params = {PARAM_MBC, + channelIndex}; + Number[] values = {0 /*0 in use */, + 0 /*1 enabled*/, + 0 /*2 band count */}; + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. + getParameter(paramBytes, valueBytes); + byteArrayToNumberArray(valueBytes, values); + int bandCount = values[2].intValue(); + Mbc mbc = new Mbc(values[0].intValue() > 0 /* in use */, + values[1].intValue() > 0 /* enabled */, + bandCount /*band count*/); + for (int b = 0; b < bandCount; b++) { + MbcBand mbcBand = queryEngineMbcBandByChannelIndex(channelIndex, b); + mbc.setBand(b, mbcBand); + } + return mbc; + } + private MbcBand queryEngineMbcBandByChannelIndex(int channelIndex, int bandIndex) { + Number[] params = {PARAM_MBC_BAND, + channelIndex, + bandIndex}; + Number[] values = {0 /*0 enabled */, + 0.0f /*1 cutoffFrequency */, + 0.0f /*2 AttackTime */, + 0.0f /*3 ReleaseTime */, + 0.0f /*4 Ratio */, + 0.0f /*5 Threshold */, + 0.0f /*6 KneeWidth */, + 0.0f /*7 NoiseGateThreshold */, + 0.0f /*8 ExpanderRatio */, + 0.0f /*9 PreGain */, + 0.0f /*10 PostGain*/}; + + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. + getParameter(paramBytes, valueBytes); + + byteArrayToNumberArray(valueBytes, values); + + return new MbcBand(values[0].intValue() > 0 /* enabled */, + values[1].floatValue() /* cutoffFrequency */, + values[2].floatValue()/*2 AttackTime */, + values[3].floatValue()/*3 ReleaseTime */, + values[4].floatValue()/*4 Ratio */, + values[5].floatValue()/*5 Threshold */, + values[6].floatValue()/*6 KneeWidth */, + values[7].floatValue()/*7 NoiseGateThreshold */, + values[8].floatValue()/*8 ExpanderRatio */, + values[9].floatValue()/*9 PreGain */, + values[10].floatValue()/*10 PostGain*/); + } + private void updateEngineMbcBandByChannelIndex(int channelIndex, int bandIndex, + @NonNull MbcBand mbcBand) { + Number[] params = { PARAM_MBC_BAND, + channelIndex, + bandIndex}; + Number[] values = {(mbcBand.isEnabled() ? 1 : 0), + mbcBand.getCutoffFrequency(), + mbcBand.getAttackTime(), + mbcBand.getReleaseTime(), + mbcBand.getRatio(), + mbcBand.getThreshold(), + mbcBand.getKneeWidth(), + mbcBand.getNoiseGateThreshold(), + mbcBand.getExpanderRatio(), + mbcBand.getPreGain(), + mbcBand.getPostGain()}; + setNumberArray(params, values); + } + + private void updateEngineMbcByChannelIndex(int channelIndex, @NonNull Mbc mbc) { + int bandCount = mbc.getBandCount(); + Number[] params = { PARAM_MBC, + channelIndex}; + Number[] values = {(mbc.isInUse() ? 1 : 0), + (mbc.isEnabled() ? 1 : 0), + bandCount}; + setNumberArray(params, values); + for (int b = 0; b < bandCount; b++) { + MbcBand mbcBand = mbc.getBand(b); + updateEngineMbcBandByChannelIndex(channelIndex, b, mbcBand); + } + } + + private void updateEngineLimiterByChannelIndex(int channelIndex, @NonNull Limiter limiter) { + Number[] params = { PARAM_LIMITER, + channelIndex}; + Number[] values = {(limiter.isInUse() ? 1 : 0), + (limiter.isEnabled() ? 1 : 0), + limiter.getLinkGroup(), + limiter.getAttackTime(), + limiter.getReleaseTime(), + limiter.getRatio(), + limiter.getThreshold(), + limiter.getPostGain()}; + setNumberArray(params, values); + } + + private Limiter queryEngineLimiterByChannelIndex(int channelIndex) { + Number[] params = {PARAM_LIMITER, + channelIndex}; + Number[] values = {0 /*0 in use (int)*/, + 0 /*1 enabled (int)*/, + 0 /*2 link group (int)*/, + 0.0f /*3 attack time (float)*/, + 0.0f /*4 release time (float)*/, + 0.0f /*5 ratio (float)*/, + 0.0f /*6 threshold (float)*/, + 0.0f /*7 post gain(float)*/}; + + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. + getParameter(paramBytes, valueBytes); + byteArrayToNumberArray(valueBytes, values); + + return new Limiter(values[0].intValue() > 0 /*in use*/, + values[1].intValue() > 0 /*enabled*/, + values[2].intValue() /*linkGroup*/, + values[3].floatValue() /*attackTime*/, + values[4].floatValue() /*releaseTime*/, + values[5].floatValue() /*ratio*/, + values[6].floatValue() /*threshold*/, + values[7].floatValue() /*postGain*/); + } + + private Channel queryEngineByChannelIndex(int channelIndex) { + float inputGain = getTwoFloat(PARAM_INPUT_GAIN, channelIndex); + Eq preEq = queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex); + Mbc mbc = queryEngineMbcByChannelIndex(channelIndex); + Eq postEq = queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex); + Limiter limiter = queryEngineLimiterByChannelIndex(channelIndex); + + Channel channel = new Channel(inputGain, + preEq.isInUse(), preEq.getBandCount(), + mbc.isInUse(), mbc.getBandCount(), + postEq.isInUse(), postEq.getBandCount(), + limiter.isInUse()); + channel.setInputGain(inputGain); + channel.setPreEq(preEq); + channel.setMbc(mbc); + channel.setPostEq(postEq); + channel.setLimiter(limiter); + return channel; + } + + private void updateEngineChannelByChannelIndex(int channelIndex, @NonNull Channel channel) { + //send things with as few calls as possible + setTwoFloat(PARAM_INPUT_GAIN, channelIndex, channel.getInputGain()); + Eq preEq = channel.getPreEq(); + updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq); + Mbc mbc = channel.getMbc(); + updateEngineMbcByChannelIndex(channelIndex, mbc); + Eq postEq = channel.getPostEq(); + updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq); + Limiter limiter = channel.getLimiter(); + updateEngineLimiterByChannelIndex(channelIndex, limiter); } //****** convenience methods: // - private int getOneInt(int paramGet) { - int[] param = new int[1]; - int[] result = new int[1]; + private int getOneInt(int param) { + final int[] params = { param }; + final int[] result = new int[1]; - param[0] = paramGet; - checkStatus(getParameter(param, result)); + checkStatus(getParameter(params, result)); return result[0]; } - private int getTwoInt(int paramGet, int paramA) { - int[] param = new int[2]; - int[] result = new int[1]; + private void setTwoFloat(int param, int paramA, float valueSet) { + final int[] params = { param, paramA }; + final byte[] value; - param[0] = paramGet; - param[1] = paramA; - checkStatus(getParameter(param, result)); - return result[0]; - } - - private int getThreeInt(int paramGet, int paramA, int paramB) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramGet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] resultBytes = new byte[4]; //single int - - checkStatus(getParameter(paramBytes, resultBytes)); - - return byteArrayToInt(resultBytes); - } - - private void setOneInt(int paramSet, int valueSet) { - int[] param = new int[1]; - int[] value = new int[1]; - - param[0] = paramSet; - value[0] = valueSet; - checkStatus(setParameter(param, value)); - } - - private void setTwoInt(int paramSet, int paramA, int valueSet) { - int[] param = new int[2]; - int[] value = new int[1]; - - param[0] = paramSet; - param[1] = paramA; - value[0] = valueSet; - checkStatus(setParameter(param, value)); - } - - private void setThreeInt(int paramSet, int paramA, int paramB, int valueSet) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramSet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] valueBytes = intToByteArray(valueSet); - - checkStatus(setParameter(paramBytes, valueBytes)); - } - - private void setOneFloat(int paramSet, float valueSet) { - int[] param = new int[1]; - byte[] value; - - param[0] = paramSet; - value = floatToByteArray(valueSet); - checkStatus(setParameter(param, value)); - } - - private void setTwoFloat(int paramSet, int paramA, float valueSet) { - int[] param = new int[2]; - byte[] value; - - param[0] = paramSet; - param[1] = paramA; value = floatToByteArray(valueSet); - checkStatus(setParameter(param, value)); + checkStatus(setParameter(params, value)); } - private void setThreeFloat(int paramSet, int paramA, int paramB, float valueSet) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramSet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] valueBytes = floatToByteArray(valueSet); - - checkStatus(setParameter(paramBytes, valueBytes)); - } - private byte[] intArrayToByteArray(int[] values) { - int expectedBytes = values.length * 4; + private byte[] numberArrayToByteArray(Number[] values) { + int expectedBytes = 0; + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof Integer) { + expectedBytes += Integer.BYTES; + } else if (values[i] instanceof Float) { + expectedBytes += Float.BYTES; + } else { + throw new IllegalArgumentException("unknown value type " + + values[i].getClass()); + } + } ByteBuffer converter = ByteBuffer.allocate(expectedBytes); converter.order(ByteOrder.nativeOrder()); - for (int k = 0; k < values.length; k++) { - converter.putFloat(values[k]); + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof Integer) { + converter.putInt(values[i].intValue()); + } else if (values[i] instanceof Float) { + converter.putFloat(values[i].floatValue()); + } } return converter.array(); } - private void setIntArray(int paramSet, int[] paramArray) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = intToByteArray(paramSet); - byte[] valueBytes = intArrayToByteArray(paramArray); - checkStatus(setParameter(paramBytes, valueBytes)); + private void byteArrayToNumberArray(byte[] valuesIn, Number[] valuesOut) { + int inIndex = 0; + int outIndex = 0; + while (inIndex < valuesIn.length && outIndex < valuesOut.length) { + if (valuesOut[outIndex] instanceof Integer) { + valuesOut[outIndex++] = byteArrayToInt(valuesIn, inIndex); + inIndex += Integer.BYTES; + } else if (valuesOut[outIndex] instanceof Float) { + valuesOut[outIndex++] = byteArrayToFloat(valuesIn, inIndex); + inIndex += Float.BYTES; + } else { + throw new IllegalArgumentException("can't convert " + + valuesOut[outIndex].getClass()); + } + } + if (outIndex != valuesOut.length) { + throw new IllegalArgumentException("only converted " + outIndex + + " values out of "+ valuesOut.length + " expected"); + } } - private float getOneFloat(int paramGet) { - int[] param = new int[1]; - byte[] result = new byte[4]; - - param[0] = paramGet; - checkStatus(getParameter(param, result)); - return byteArrayToFloat(result); + private void setNumberArray(Number[] params, Number[] values) { + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); + checkStatus(setParameter(paramBytes, valueBytes)); } - private float getTwoFloat(int paramGet, int paramA) { - int[] param = new int[2]; - byte[] result = new byte[4]; + private float getTwoFloat(int param, int paramA) { + final int[] params = { param, paramA }; + final byte[] result = new byte[4]; - param[0] = paramGet; - param[1] = paramA; - checkStatus(getParameter(param, result)); + checkStatus(getParameter(params, result)); return byteArrayToFloat(result); } - private float getThreeFloat(int paramGet, int paramA, int paramB) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramGet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] resultBytes = new byte[4]; //single float - - checkStatus(getParameter(paramBytes, resultBytes)); - - return byteArrayToFloat(resultBytes); - } - - private float[] getOneFloatArray(int paramGet, int expectedSize) { - int[] param = new int[1]; - byte[] result = new byte[4 * expectedSize]; - - param[0] = paramGet; - checkStatus(getParameter(param, result)); - float[] returnArray = new float[expectedSize]; - for (int k = 0; k < expectedSize; k++) { - returnArray[k] = byteArrayToFloat(result, 4 * k); - } - return returnArray; - } /** * @hide * The OnParameterChangeListener interface defines a method called by the DynamicsProcessing diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 82bc6af870f0..6368607d7bdd 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -543,30 +543,30 @@ <!-- UI debug setting: Select Bluetooth AVRCP Version --> <string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string> - <!-- UI debug setting: Select Bluetooth Audio Codec --> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection --> <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string> - <!-- UI debug setting: Select Bluetooth Audio Codec --> - <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection --> + <string name="bluetooth_select_a2dp_codec_type_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection</string> - <!-- UI debug setting: Select Bluetooth Audio Sample Rate --> + <!-- UI debug setting: Trigger Bluetooth Audio Sample Rate Selection --> <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string> - <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate --> - <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Sample Rate --> + <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Sample Rate</string> - <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample --> + <!-- UI debug setting: Trigger Bluetooth Audio Bits Per Sample Selection --> <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string> - <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample --> - <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Bits Per Sample --> + <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Bits Per Sample</string> - <!-- UI debug setting: Select Bluetooth Audio Channel Mode --> + <!-- UI debug setting: Trigger Bluetooth Audio Channel Mode Selection --> <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string> - <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode --> - <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Channel Mode --> + <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Channel Mode</string> - <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality --> + <!-- UI debug setting: Trigger Bluetooth Audio LDAC Playback Quality Selection --> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string> <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality --> - <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string> + <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Trigger Bluetooth Audio LDAC Codec\u000ASelection: Playback Quality</string> <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming --> <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string> diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index 28833a349372..835ff07c4006 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -22,8 +22,9 @@ import android.content.Intent; import android.os.PowerManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; -import android.support.annotation.VisibleForTesting; +import android.util.KeyValueListParser; import android.util.Log; +import android.util.Slog; /** * Utilities related to battery saver. @@ -48,13 +49,35 @@ public class BatterySaverUtils { public static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION = "PNW.autoSaverSuggestion"; - /** - * We show the auto battery saver suggestion notification when the user manually enables - * battery saver for the START_NTH time through the END_NTH time. - * (We won't show it for END_NTH + 1 time and after.) - */ - private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4; - private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8; + private static class Parameters { + private final Context mContext; + + /** + * We show the auto battery saver suggestion notification when the user manually enables + * battery saver for the START_NTH time through the END_NTH time. + * (We won't show it for END_NTH + 1 time and after.) + */ + private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4; + private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8; + + public final int startNth; + public final int endNth; + + public Parameters(Context context) { + mContext = context; + + final String newValue = Global.getString(mContext.getContentResolver(), + Global.LOW_POWER_MODE_SUGGESTION_PARAMS); + final KeyValueListParser parser = new KeyValueListParser(','); + try { + parser.setString(newValue); + } catch (IllegalArgumentException e) { + Slog.wtf(TAG, "Bad constants: " + newValue); + } + startNth = parser.getInt("start_nth", AUTO_SAVER_SUGGESTION_START_NTH); + endNth = parser.getInt("end_nth", AUTO_SAVER_SUGGESTION_END_NTH); + } + } /** * Enable / disable battery saver by user request. @@ -85,8 +108,10 @@ public class BatterySaverUtils { Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1; Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count); - if ((count >= AUTO_SAVER_SUGGESTION_START_NTH) - && (count <= AUTO_SAVER_SUGGESTION_END_NTH) + final Parameters parameters = new Parameters(context); + + if ((count >= parameters.startNth) + && (count <= parameters.endNth) && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0 && Secure.getInt(cr, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java index 53f7e44bc25a..ad300f43d88d 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java @@ -50,4 +50,6 @@ public abstract class QSTileView extends LinearLayout { public abstract void onStateChanged(State state); public abstract int getDetailY(); + + public void setExpansion(float expansion) {} } diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml index 12d8016bf6a0..e15a734b0e05 100644 --- a/packages/SystemUI/res/drawable/qs_customizer_background.xml +++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml @@ -14,6 +14,6 @@ limitations under the License. --> <transition xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@color/qs_detail_transition" /> - <item android:drawable="?android:attr/colorPrimary" /> + <item android:drawable="@drawable/qs_customizer_background_transition" /> + <item android:drawable="@drawable/qs_customizer_background_primary" /> </transition> diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml new file mode 100644 index 000000000000..abe1429697ac --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <shape> + <solid android:color="?android:attr/colorPrimary"/> + <corners android:radius="?android:attr/dialogCornerRadius" /> + </shape> +</inset> diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml new file mode 100644 index 000000000000..ed8f61a97c2a --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml @@ -0,0 +1,21 @@ +<?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. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <shape> + <solid android:color="@color/qs_detail_transition"/> + <corners android:radius="?android:attr/dialogCornerRadius" /> + </shape> +</inset> diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml new file mode 100644 index 000000000000..557cae150303 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android"> + <shape> + <solid android:color="?android:attr/colorSecondary"/> + <corners + android:topLeftRadius="?android:attr/dialogCornerRadius" + android:topRightRadius="?android:attr/dialogCornerRadius" /> + </shape> +</inset> diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml index 71ad85bf3a96..51febc78e23e 100644 --- a/packages/SystemUI/res/layout/qs_customize_divider.xml +++ b/packages/SystemUI/res/layout/qs_customize_divider.xml @@ -20,9 +20,8 @@ android:id="@android:id/title" android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center" android:paddingTop="20dp" - android:paddingStart="16dp" - android:paddingEnd="8dp" android:paddingBottom="13dp" android:textAppearance="@android:style/TextAppearance.Material.Body2" android:textColor="?android:attr/colorAccent" diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml index b3b6a0c43a98..506e6c856aff 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel.xml @@ -22,7 +22,6 @@ android:layout_height="0dp" android:elevation="4dp" android:orientation="vertical" - android:background="@drawable/qs_customizer_background" android:gravity="center_horizontal"> </com.android.systemui.qs.customize.QSCustomizer> diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml index 04d0e6524eac..d70a37ae15b2 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml @@ -15,24 +15,44 @@ limitations under the License. --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - - <Toolbar - android:id="@*android:id/action_bar" +<merge xmlns:android="http://schemas.android.com/apk/res/android">-> + <View android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="28dp" - android:navigationContentDescription="@*android:string/action_bar_up_description" - style="?android:attr/toolbarStyle" /> + android:layout_height="@*android:dimen/quick_qs_offset_height" + android:background="@android:color/transparent" /> - <android.support.v7.widget.RecyclerView - android:id="@android:id/list" + <com.android.keyguard.AlphaOptimizedLinearLayout + android:id="@+id/customize_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" - android:scrollIndicators="top" - android:scrollbars="vertical" - android:importantForAccessibility="no" /> + android:layout_marginLeft="@dimen/notification_side_paddings" + android:layout_marginRight="@dimen/notification_side_paddings" + android:orientation="vertical" + android:background="@drawable/qs_customizer_background"> + <Toolbar + android:id="@*android:id/action_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/qs_customizer_toolbar" + android:navigationContentDescription="@*android:string/action_bar_up_description" + style="?android:attr/toolbarStyle" /> + + <android.support.v7.widget.RecyclerView + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:paddingTop="28dp" + android:paddingLeft="@dimen/qs_tile_layout_margin_side" + android:paddingRight="@dimen/qs_tile_layout_margin_side" + android:paddingBottom="28dp" + android:clipToPadding="false" + android:scrollIndicators="top" + android:scrollbars="vertical" + android:scrollbarStyle="outsideOverlay" + android:importantForAccessibility="no" /> + </com.android.keyguard.AlphaOptimizedLinearLayout> <View android:id="@+id/nav_bar_background" diff --git a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml index ff55f990857a..a2250b1975a3 100644 --- a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml +++ b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml @@ -17,9 +17,8 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_height="wrap_content" + android:layout_height="@dimen/qs_tile_height" android:layout_width="match_parent" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:paddingTop="8dp" + android:layout_marginTop="@dimen/qs_tile_margin_top_bottom" + android:layout_marginBottom="@dimen/qs_tile_margin_top_bottom" android:gravity="center" /> diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index cd3271fa5374..c03f25c75101 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -21,7 +21,7 @@ android:layout_marginTop="8dp" android:layout_marginBottom="14dp" android:layout_below="@id/quick_status_bar_system_icons" - > + android:paddingEnd="@dimen/signal_cluster_battery_padding" > <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" @@ -29,9 +29,4 @@ android:layout_height="match_parent" android:layout_weight="1" /> - <include layout="@layout/signal_cluster_view" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginStart="@dimen/signal_cluster_margin_start" /> - </LinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml new file mode 100644 index 000000000000..d607c8c29ead --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<com.android.systemui.statusbar.StatusBarMobileView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/mobile_combo" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:orientation="horizontal"> + <FrameLayout + android:id="@+id/inout_container" + android:layout_height="17dp" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical"> + <ImageView + android:id="@+id/mobile_in" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_down" + android:visibility="gone" + android:paddingEnd="2dp" + /> + <ImageView + android:id="@+id/mobile_out" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_up" + android:paddingEnd="2dp" + android:visibility="gone" + /> + </FrameLayout> + <ImageView + android:id="@+id/mobile_type" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical" + android:paddingEnd="1dp" + android:visibility="gone" /> + <Space + android:id="@+id/mobile_roaming_space" + android:layout_height="match_parent" + android:layout_width="@dimen/roaming_icon_start_padding" + android:visibility="gone" + /> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> + <com.android.systemui.statusbar.AnimatedImageView + android:id="@+id/mobile_signal" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + systemui:hasOverlappingRendering="false" + /> + <ImageView + android:id="@+id/mobile_roaming" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> + </FrameLayout> +</com.android.systemui.statusbar.StatusBarMobileView> + diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml new file mode 100644 index 000000000000..08cef55e0393 --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_wifi_group.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<com.android.systemui.statusbar.StatusBarWifiView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/wifi_combo" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="4dp" + android:gravity="center_vertical" + android:orientation="horizontal" > + + <FrameLayout + android:id="@+id/inout_container" + android:layout_height="17dp" + android:layout_width="wrap_content" + android:gravity="center_vertical" > + <ImageView + android:id="@+id/wifi_in" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_down" + android:visibility="gone" + android:paddingEnd="2dp" + /> + <ImageView + android:id="@+id/wifi_out" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_up" + android:paddingEnd="2dp" + android:visibility="gone" + /> + </FrameLayout> + <FrameLayout + android:id="@+id/wifi_combo" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:gravity="center_vertical" > + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:theme="?attr/lightIconTheme" + android:id="@+id/wifi_signal" + android:layout_height="wrap_content" + android:layout_width="wrap_content" /> + </FrameLayout> + + <View + android:id="@+id/wifi_signal_spacer" + android:layout_width="@dimen/status_bar_wifi_signal_spacer_width" + android:layout_height="4dp" + android:visibility="gone" /> + + <!-- Looks like CarStatusBar uses this... --> + <ViewStub + android:id="@+id/connected_device_signals_stub" + android:layout="@layout/connected_device_signal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <View + android:id="@+id/wifi_airplane_spacer" + android:layout_width="@dimen/status_bar_airplane_spacer_width" + android:layout_height="4dp" + android:visibility="gone" + /> +</com.android.systemui.statusbar.StatusBarWifiView> diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index 1fafb2fc72d6..258b82aacb2e 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -24,14 +24,10 @@ android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" + android:paddingEnd="4dp" android:gravity="center_vertical" android:orientation="horizontal"/> - <include layout="@layout/signal_cluster_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/signal_cluster_margin_start"/> - <com.android.systemui.BatteryMeterView android:id="@+id/battery" android:layout_height="match_parent" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0f07ca45bc7f..84ca657f3da5 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -317,8 +317,10 @@ <dimen name="pull_span_min">25dp</dimen> <dimen name="qs_tile_height">106dp</dimen> + <dimen name="qs_tile_layout_margin_side">9dp</dimen> <dimen name="qs_tile_margin_horizontal">18dp</dimen> <dimen name="qs_tile_margin_vertical">24dp</dimen> + <dimen name="qs_tile_margin_top_bottom">12dp</dimen> <dimen name="qs_tile_margin_top">18dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> <!-- Width for the spacer, used between QS tiles. --> diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml index 5679dd2f1fb0..6caed61f11a7 100644 --- a/packages/SystemUI/res/values/dimens_car.xml +++ b/packages/SystemUI/res/values/dimens_car.xml @@ -32,6 +32,8 @@ <dimen name="car_navigation_button_width">64dp</dimen> <dimen name="car_navigation_bar_width">760dp</dimen> + <dimen name="car_left_navigation_bar_width">96dp</dimen> + <dimen name="car_right_navigation_bar_width">96dp</dimen> <dimen name="car_page_indicator_dot_diameter">12dp</dimen> <dimen name="car_page_indicator_margin_bottom">24dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e9b2be9b5b3e..4074042d3615 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1748,6 +1748,10 @@ <!-- Name of the headset in status bar [CHAR LIMIT=30] --> <string name="headset">Headset</string> + <!-- Accessibility description for long click on a quick settings tile - this is used in the + context of the sentence "double tap and hold to _Open settings_" [CHAR LIMIT=NONE] --> + <string name="accessibility_long_click_tile">Open settings</string> + <!-- Accessibility description of headphones icon [CHAR LIMIT=NONE] --> <string name="accessibility_status_bar_headphones">Headphones connected</string> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 2c0e95b5af26..a61ce8c291fd 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -29,30 +29,16 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.util.ArraySet; -import android.util.TimingsTraceLog; import android.util.Log; +import android.util.TimingsTraceLog; -import com.android.systemui.globalactions.GlobalActionsComponent; -import com.android.systemui.keyboard.KeyboardUI; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.media.RingtonePlayer; -import com.android.systemui.pip.PipUI; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; -import com.android.systemui.power.PowerUI; -import com.android.systemui.recents.Recents; -import com.android.systemui.shortcut.ShortcutKeyDispatcher; -import com.android.systemui.stackdivider.Divider; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowManager; -import com.android.systemui.usb.StorageNotification; import com.android.systemui.util.NotificationChannels; -import com.android.systemui.util.leak.GarbageMonitor; -import com.android.systemui.volume.VolumeUI; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -99,6 +85,10 @@ public class SystemUIApplication extends Application implements SysUiServiceProv mServices[i].onBootCompleted(); } } + + IntentFilter localeChangedFilter = new IntentFilter( + Intent.ACTION_LOCALE_CHANGED); + registerReceiver(mLocaleChangeReceiver, localeChangedFilter); } }, filter); } else { @@ -249,4 +239,14 @@ public class SystemUIApplication extends Application implements SysUiServiceProv public SystemUI[] getServices() { return mServices; } + + private final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { + // Update names of SystemUi notification channels + NotificationChannels.createAll(context); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index f08219a8f742..c409f738ec2a 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -52,6 +52,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.time.Duration; import java.util.Arrays; public class PowerUI extends SystemUI { @@ -61,6 +62,8 @@ public class PowerUI extends SystemUI { private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS; private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3; + private static final int CHARGE_CYCLE_PERCENT_RESET = 45; + private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis(); private final Handler mHandler = new Handler(); private final Receiver mReceiver = new Receiver(); @@ -69,7 +72,6 @@ public class PowerUI extends SystemUI { private HardwarePropertiesManager mHardwarePropertiesManager; private WarningsUI mWarnings; private final Configuration mLastConfiguration = new Configuration(); - private int mBatteryLevel = 100; private long mTimeRemaining = Long.MAX_VALUE; private int mPlugType = 0; private int mInvalidCharger = 0; @@ -88,6 +90,7 @@ public class PowerUI extends SystemUI { private long mNextLogTime; private IThermalService mThermalService; + @VisibleForTesting int mBatteryLevel = 100; @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; // by using the same instance (method references are not guaranteed to be the same object @@ -205,12 +208,6 @@ public class PowerUI extends SystemUI { final boolean plugged = mPlugType != 0; final boolean oldPlugged = oldPlugType != 0; - // if we are now unplugged but we were previously plugged in we should allow the - // time based trigger again. - if (!plugged && plugged != oldPlugged) { - mLowWarningShownThisChargeCycle = false; - mSevereWarningShownThisChargeCycle = false; - } int oldBucket = findBatteryLevelBucket(oldBatteryLevel); int bucket = findBatteryLevelBucket(mBatteryLevel); @@ -261,7 +258,8 @@ public class PowerUI extends SystemUI { boolean isPowerSaver = mPowerManager.isPowerSaveMode(); // only play SFX when the dialog comes up or the bucket changes final boolean playSound = bucket != oldBucket || oldPlugged; - if (mEnhancedEstimates.isHybridNotificationEnabled()) { + final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled(); + if (hybridEnabled) { final Estimate estimate = mEnhancedEstimates.getEstimate(); // Turbo is not always booted once SysUI is running so we have ot make sure we actually // get data back @@ -270,6 +268,14 @@ public class PowerUI extends SystemUI { mWarnings.updateEstimate(estimate); mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(), mEnhancedEstimates.getSevereWarningThreshold()); + + // if we are now over 45% battery & 6 hours remaining we can trigger hybrid + // notification again + if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET + && mTimeRemaining > SIX_HOURS_MILLIS) { + mLowWarningShownThisChargeCycle = false; + mSevereWarningShownThisChargeCycle = false; + } } } @@ -277,13 +283,15 @@ public class PowerUI extends SystemUI { mTimeRemaining, isPowerSaver, mBatteryStatus)) { mWarnings.showLowBatteryWarning(playSound); - // mark if we've already shown a warning this cycle. This will prevent the time based - // trigger from spamming users since the time remaining can vary based on current - // device usage. - if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()) { - mSevereWarningShownThisChargeCycle = true; - } else { - mLowWarningShownThisChargeCycle = true; + // mark if we've already shown a warning this cycle. This will prevent the notification + // trigger from spamming users by only showing low/critical warnings once per cycle + if (hybridEnabled) { + if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold() + || mBatteryLevel < mLowBatteryReminderLevels[1]) { + mSevereWarningShownThisChargeCycle = true; + } else { + mLowWarningShownThisChargeCycle = true; + } } } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining, isPowerSaver)) { @@ -295,12 +303,16 @@ public class PowerUI extends SystemUI { @VisibleForTesting boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket, - int bucket, long timeRemaining, boolean isPowerSaver, int mBatteryStatus) { + int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) { + if (mEnhancedEstimates.isHybridNotificationEnabled()) { + // triggering logic when enhanced estimate is available + return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus); + } + // legacy triggering logic return !plugged && !isPowerSaver - && (((bucket < oldBucket || oldPlugged) && bucket < 0) - || isTimeBasedTrigger(timeRemaining)) - && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN; + && (((bucket < oldBucket || oldPlugged) && bucket < 0)) + && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN; } @VisibleForTesting @@ -315,19 +327,23 @@ public class PowerUI extends SystemUI { || hybridWouldDismiss)); } - private boolean isTimeBasedTrigger(long timeRemaining) { - if (!mEnhancedEstimates.isHybridNotificationEnabled()) { + private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver, + int batteryStatus) { + if (plugged || isPowerSaver || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { return false; } - - // Only show the time based warning once per charge cycle - final boolean canShowWarning = timeRemaining < mEnhancedEstimates.getLowWarningThreshold() - && !mLowWarningShownThisChargeCycle; - - // Only show the severe time based warning once per charge cycle - final boolean canShowSevereWarning = - timeRemaining < mEnhancedEstimates.getSevereWarningThreshold() - && !mSevereWarningShownThisChargeCycle; + int warnLevel = mLowBatteryReminderLevels[0]; + int critLevel = mLowBatteryReminderLevels[1]; + + // Only show the low warning once per charge cycle + final boolean canShowWarning = !mLowWarningShownThisChargeCycle + && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold() + || mBatteryLevel <= warnLevel); + + // Only show the severe warning once per charge cycle + final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle + && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold() + || mBatteryLevel <= critLevel); return canShowWarning || canShowSevereWarning; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 892395222d1f..c548cf6a6519 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -167,6 +167,13 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } } + @Override + public void setExpansion(float expansion) { + for (TileRecord tr : mTiles) { + tr.tileView.setExpansion(expansion); + } + } + public void setPageListener(PageListener listener) { mPageListener = listener; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 29f3c43a1fa4..018a63560429 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -291,6 +291,7 @@ public class QSFragment extends Fragment implements QS { mHeader.setExpansion(mKeyguardShowing, expansion, panelTranslationY); mFooter.setExpansion(mKeyguardShowing ? 1 : expansion); mQSPanel.getQsTileRevealController().setExpansion(expansion); + mQSPanel.getTileLayout().setExpansion(expansion); mQSPanel.setTranslationY(translationScaleY * heightDiff); mQSDetail.setFullyExpanded(fullyExpanded); @@ -359,7 +360,6 @@ public class QSFragment extends Fragment implements QS { // The customize state changed, so our height changed. mContainer.updateExpansion(); mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE); - mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE); mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE); // Let the panel know the position changed and it needs to update where notifications // and whatnot are. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 61e3065fd4a3..6368a6b32787 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -616,5 +616,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne boolean updateResources(); void setListening(boolean listening); + + default void setExpansion(float expansion) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 0ac8b9ccef7e..df65d1fb82cd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -47,7 +47,6 @@ import com.android.systemui.BatteryMeterView; import com.android.systemui.Dependency; import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.R.id; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSDetail.Callback; @@ -161,7 +160,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue // Set light text on the header icons because they will always be on a black background applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT); - applyDarkness(id.signal_cluster, tintArea, intensity, colorForeground); // Set the correct tint for the status icons so they contrast mIconManager.setTint(fillColor); diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 1cb89c472dbb..64e7a6353485 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -23,6 +23,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mCellHeight; protected int mCellMarginHorizontal; protected int mCellMarginVertical; + protected int mSidePadding; protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); private int mCellMarginTop; @@ -80,6 +81,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); + mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side); if (mColumns != columns) { mColumns = columns; requestLayout(); @@ -93,7 +95,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { final int numTiles = mRecords.size(); final int width = MeasureSpec.getSize(widthMeasureSpec); final int numRows = (numTiles + mColumns - 1) / mColumns; - mCellWidth = (width - (mCellMarginHorizontal * (mColumns + 1))) / mColumns; + mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns; // Measure each QS tile. View previousView = this; diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java index ec183769c763..31c455d880d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java @@ -44,7 +44,6 @@ public class CarStatusBarHeader extends LinearLayout { float intensity = colorForeground == Color.WHITE ? 0f : 1f; Rect tintArea = new Rect(0, 0, 0, 0); - applyDarkness(R.id.signal_cluster, tintArea, intensity, colorForeground); applyDarkness(R.id.battery, tintArea, intensity, colorForeground); applyDarkness(R.id.clock, tintArea, intensity, colorForeground); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index a3d6c6cff283..4aa83d0d5f15 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -20,6 +20,7 @@ import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Point; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; @@ -37,6 +38,7 @@ import android.widget.Toolbar.OnMenuItemClickListener; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QS; @@ -81,10 +83,9 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public QSCustomizer(Context context, AttributeSet attrs) { super(new ContextThemeWrapper(context, R.style.edit_theme), attrs); - mClipper = new QSDetailClipper(this); LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this); - + mClipper = new QSDetailClipper(findViewById(R.id.customize_container)); mToolbar = findViewById(com.android.internal.R.id.action_bar); TypedValue value = new TypedValue(); mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true); @@ -100,7 +101,10 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, mContext.getString(com.android.internal.R.string.reset)); mToolbar.setTitle(R.string.qs_edit); - + int accentColor = Utils.getColorAttr(context, android.R.attr.colorAccent); + mToolbar.setTitleTextColor(accentColor); + mToolbar.getNavigationIcon().setTint(accentColor); + mToolbar.getOverflowIcon().setTint(accentColor); mRecyclerView = findViewById(android.R.id.list); mTileAdapter = new TileAdapter(getContext()); mTileQueryHelper = new TileQueryHelper(context, mTileAdapter); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index 943a17644b55..6f664d7debc0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -102,7 +102,9 @@ public class TileServices extends IQSService.Stub { mTokenMap.remove(service.getToken()); mTiles.remove(tile.getComponent()); final String slot = tile.getComponent().getClassName(); - mMainHandler.post(() -> mHost.getIconController().removeIcon(slot)); + // TileServices doesn't know how to add more than 1 icon per slot, so remove all + mMainHandler.post(() -> mHost.getIconController() + .removeAllIconsForSlot(slot)); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index a9defc8b75a0..09d928fd3657 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -194,6 +194,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { setClickable(state.state != Tile.STATE_UNAVAILABLE); mIcon.setIcon(state); setContentDescription(state.contentDescription); + mAccessibilityClass = state.expandedAccessibilityClassName; if (state instanceof QSTile.BooleanState) { boolean newState = ((BooleanState) state).value; @@ -269,6 +270,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { info.setText(label); info.setChecked(b); info.setCheckable(true); + info.addAction( + new AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.getId(), + getResources().getString(R.string.accessibility_long_click_tile))); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index 3cb4c71fc98c..d21b06f02f16 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -32,12 +32,10 @@ import com.android.systemui.R; import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; - import java.util.Objects; /** View that represents a standard quick settings tile. **/ public class QSTileView extends QSTileBaseView { - private static final int DEFAULT_MAX_LINES = 2; private static final boolean DUAL_TARGET_ALLOWED = false; private View mDivider; protected TextView mLabel; @@ -87,22 +85,17 @@ public class QSTileView extends QSTileBaseView { mLabelContainer.setClipChildren(false); mLabelContainer.setClipToPadding(false); mLabel = mLabelContainer.findViewById(R.id.tile_label); - mLabel.setSelected(true); // Allow marquee to work. mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock); mDivider = mLabelContainer.findViewById(R.id.underline); mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator); mExpandSpace = mLabelContainer.findViewById(R.id.expand_space); mSecondLine = mLabelContainer.findViewById(R.id.app_label); mSecondLine.setAlpha(.6f); - mSecondLine.setSelected(true); // Allow marquee to work. addView(mLabelContainer); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mLabel.getMaxLines() != DEFAULT_MAX_LINES) { - mLabel.setMaxLines(DEFAULT_MAX_LINES); - } super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Remeasure view if the secondary label text will be cut off. @@ -114,6 +107,15 @@ public class QSTileView extends QSTileBaseView { } @Override + public void setExpansion(float expansion) { + // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for + // other expansion ratios since there is no way way to pause the marquee. + boolean selected = expansion == 1f ? true : expansion == 0f ? false : mLabel.isSelected(); + mLabel.setSelected(selected); + mSecondLine.setSelected(selected); + } + + @Override protected void handleStateChanged(QSTile.State state) { super.handleStateChanged(state); if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 674ccd8c3471..0f85c5b37f65 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -492,6 +492,13 @@ public class Recents extends SystemUI } } mDraggingInRecentsCurrentUser = currentUser; + + if (mOverviewProxyService.getProxy() != null) { + // The overview service is handling split screen, so just skip the wait for the + // first draw and notify the divider to start animating now + EventBus.getDefault().post(new RecentsDrawnEvent()); + } + return true; } else { EventBus.getDefault().send(new ShowUserToastEvent( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 03b263d2ae95..3ece2f958100 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback; @@ -25,7 +24,10 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.Nullable; +import android.app.NotificationChannel; import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Path; @@ -39,6 +41,7 @@ import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Log; import android.util.MathUtils; import android.util.Property; import android.view.KeyEvent; @@ -90,12 +93,17 @@ import java.util.List; import java.util.function.BooleanSupplier; import java.util.function.Consumer; +/** + * View representing a notification item - this can be either the individual child notification or + * the group summary (which contains 1 or more child notifications). + */ public class ExpandableNotificationRow extends ActivatableNotificationView implements PluginListener<NotificationMenuRowPlugin> { private static final int DEFAULT_DIVIDER_ALPHA = 0x29; private static final int COLORED_DIVIDER_ALPHA = 0x7B; private static final int MENU_VIEW_INDEX = 0; + private static final String TAG = "ExpandableNotifRow"; public interface LayoutListener { public void onLayout(); @@ -166,6 +174,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationData.Entry mEntry; private StatusBarNotification mStatusBarNotification; + private PackageManager mCachedPackageManager; + private PackageInfo mCachedPackageInfo; private String mAppName; private boolean mIsHeadsUp; private boolean mLastChronometerRunning = true; @@ -372,6 +382,53 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mEntry = entry; mStatusBarNotification = entry.notification; mNotificationInflater.inflateNotificationViews(); + + perhapsCachePackageInfo(); + } + + /** + * Caches the package manager and info objects which are expensive to obtain. + */ + private void perhapsCachePackageInfo() { + if (mCachedPackageInfo == null) { + mCachedPackageManager = StatusBar.getPackageManagerForUser( + mContext, mStatusBarNotification.getUser().getIdentifier()); + try { + mCachedPackageInfo = mCachedPackageManager.getPackageInfo( + mStatusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "perhapsCachePackageInfo: Could not find package info"); + } + } + } + + /** + * Returns whether this row is considered non-blockable (e.g. it's a non-blockable system notif, + * covers multiple channels, or is in a whitelist). + */ + public boolean getIsNonblockable() { + boolean isNonblockable; + + isNonblockable = Dependency.get(NotificationBlockingHelperManager.class) + .isNonblockablePackage(mStatusBarNotification.getPackageName()); + + // Only bother with going through the children if the row is still blockable based on the + // number of unique channels. + if (!isNonblockable) { + isNonblockable = getNumUniqueChannels() > 1; + } + + // Only bother with IPC if the package is still blockable. + if (!isNonblockable && mCachedPackageManager != null && mCachedPackageInfo != null) { + if (com.android.settingslib.Utils.isSystemPackage( + mContext.getResources(), mCachedPackageManager, mCachedPackageInfo)) { + if (mEntry.channel != null + && !mEntry.channel.isBlockableSystem()) { + isNonblockable = true; + } + } + } + return isNonblockable; } public void onNotificationUpdated() { @@ -2019,6 +2076,32 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateChildrenVisibility(); applyChildrenRoundness(); } + /** + * Returns the number of channels covered by the notification row (including its children if + * it's a summary notification). + */ + public int getNumUniqueChannels() { + ArraySet<NotificationChannel> channels = new ArraySet<>(); + + channels.add(mEntry.channel); + + // If this is a summary, then add in the children notification channels for the + // same user and pkg. + if (mIsSummaryWithChildren) { + final List<ExpandableNotificationRow> childrenRows = getNotificationChildren(); + final int numChildren = childrenRows.size(); + for (int i = 0; i < numChildren; i++) { + final ExpandableNotificationRow childRow = childrenRows.get(i); + final NotificationChannel childChannel = childRow.getEntry().channel; + final StatusBarNotification childSbn = childRow.getStatusBarNotification(); + if (childSbn.getUser().equals(mStatusBarNotification.getUser()) && + childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) { + channels.add(childChannel); + } + } + } + return channels.size(); + } public void updateChildrenHeaderAppearance() { if (mIsSummaryWithChildren) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java new file mode 100644 index 000000000000..cdb0514a2686 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java @@ -0,0 +1,78 @@ +/* + * 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 com.android.systemui.statusbar; + +import android.content.Context; +import android.content.res.Resources.Theme; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.view.ContextThemeWrapper; +import android.view.Gravity; +import com.android.settingslib.Utils; +import com.android.systemui.R; + +/** + * NeutralGoodDrawable implements a drawable that will load 2 underlying drawable resources, one + * with each the DualToneDarkTheme and DualToneLightTheme, choosing which one based on what + * DarkIconDispatcher tells us about darkness + */ +public class NeutralGoodDrawable extends LayerDrawable { + + public static NeutralGoodDrawable create(Context context, int resId) { + int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme); + int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme); + ContextThemeWrapper light = new ContextThemeWrapper(context, dualToneLightTheme); + ContextThemeWrapper dark = new ContextThemeWrapper(context, dualToneDarkTheme); + + return create(light, dark, resId); + } + + /** + * For the on-the-go young entrepreneurial who wants to cache contexts + * @param light - a context using the R.attr.lightIconTheme + * @param dark - a context using the R.attr.darkIconTheme + * @param resId - the resId for our drawable + */ + public static NeutralGoodDrawable create(Context light, Context dark, int resId) { + return new NeutralGoodDrawable( + new Drawable[] { + light.getDrawable(resId).mutate(), + dark.getDrawable(resId).mutate() }); + } + + protected NeutralGoodDrawable(Drawable []drawables) { + super(drawables); + + for (int i = 0; i < drawables.length; i++) { + setLayerGravity(i, Gravity.CENTER); + } + + mutate(); + setDarkIntensity(0); + } + + public void setDarkIntensity(float intensity) { + + getDrawable(0).setAlpha((int) ((1 - intensity) * 255f)); + getDrawable(1).setAlpha((int) (intensity * 255f)); + + invalidateSelf(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java index c9c1bc6fdc39..20e5f86ee097 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java @@ -17,11 +17,19 @@ package com.android.systemui.statusbar; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.statusbar.phone.StatusBar; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; @@ -37,6 +45,7 @@ public class NotificationBlockingHelperManager { private final Context mContext; /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */ private ExpandableNotificationRow mBlockingHelperRow; + private Set<String> mNonBlockablePkgs; /** * Whether the notification shade/stack is expanded - used to determine blocking helper @@ -46,6 +55,9 @@ public class NotificationBlockingHelperManager { public NotificationBlockingHelperManager(Context context) { mContext = context; + mNonBlockablePkgs = new HashSet<>(); + Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray( + com.android.internal.R.array.config_nonBlockableNotificationPackages)); } /** @@ -59,15 +71,14 @@ public class NotificationBlockingHelperManager { */ boolean perhapsShowBlockingHelper( ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) { - int numChildren = row.getNumberOfNotificationChildren(); - // We only show the blocking helper if: - // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group + // - User sentiment is negative (DEBUG flag can bypass) // - The notification shade is fully expanded (guarantees we're not touching a HUN). - // - User sentiment is negative - if (DEBUG - || row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE + // - The row is blockable (i.e. not non-blockable) + // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group + if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG) && mIsShadeExpanded + && !row.getIsNonblockable() && (!row.isChildInGroup() || row.isOnlyChildInGroup())) { // Dismiss any current blocking helper before continuing forward (only one can be shown // at a given time). @@ -125,6 +136,13 @@ public class NotificationBlockingHelperManager { mIsShadeExpanded = expandedHeight > 0.0f; } + /** + * Returns whether the given package name is in the list of non-blockable packages. + */ + public boolean isNonblockablePackage(String packageName) { + return mNonBlockablePkgs.contains(packageName); + } + @VisibleForTesting boolean isBlockingHelperRowNull() { return mBlockingHelperRow == null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index 75204d9513fe..dff5f3814f68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -65,7 +65,6 @@ public class NotificationGutsManager implements Dumpable { private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); - private final Set<String> mNonBlockablePkgs; private final Context mContext; private final AccessibilityManager mAccessibilityManager; @@ -87,10 +86,6 @@ public class NotificationGutsManager implements Dumpable { mContext = context; Resources res = context.getResources(); - mNonBlockablePkgs = new HashSet<>(); - Collections.addAll(mNonBlockablePkgs, res.getStringArray( - com.android.internal.R.array.config_nonBlockableNotificationPackages)); - mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); } @@ -279,12 +274,12 @@ public class NotificationGutsManager implements Dumpable { iNotificationManager, packageName, row.getEntry().channel, - getNumNotificationChannels(row, packageName, userHandle), + row.getNumUniqueChannels(), sbn, mCheckSaveListener, onSettingsClick, onAppSettingsClick, - mNonBlockablePkgs, + row.getIsNonblockable(), isForBlockingHelper, row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE); } catch (RemoteException e) { @@ -293,34 +288,6 @@ public class NotificationGutsManager implements Dumpable { } /** - * @return the number of channels covered by the notification row (including its children if - * it's a summary notification). - */ - private int getNumNotificationChannels( - ExpandableNotificationRow row, String packageName, UserHandle userHandle) { - ArraySet<NotificationChannel> channels = new ArraySet<>(); - - channels.add(row.getEntry().channel); - - // If this is a summary, then add in the children notification channels for the - // same user and pkg. - if (row.isSummaryWithChildren()) { - final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren(); - final int numChildren = childrenRows.size(); - for (int i = 0; i < numChildren; i++) { - final ExpandableNotificationRow childRow = childrenRows.get(i); - final NotificationChannel childChannel = childRow.getEntry().channel; - final StatusBarNotification childSbn = childRow.getStatusBarNotification(); - if (childSbn.getUser().equals(userHandle) && - childSbn.getPackageName().equals(packageName)) { - channels.add(childChannel); - } - } - } - return channels.size(); - } - - /** * Closes guts or notification menus that might be visible and saves any changes. * * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 82ad74e26f0c..a93be00ba080 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; @@ -49,13 +48,11 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import java.util.List; -import java.util.Set; /** * The guts of a notification revealed when performing a long press. @@ -74,7 +71,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private int mStartingUserImportance; private int mChosenImportance; private boolean mIsSingleDefaultChannel; - private boolean mNonblockable; + private boolean mIsNonblockable; private StatusBarNotification mSbn; private AnimatorSet mExpandAnimation; private boolean mIsForeground; @@ -128,10 +125,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final CheckSaveListener checkSaveListener, final OnSettingsClickListener onSettingsClick, final OnAppSettingsClickListener onAppSettingsClick, - final Set<String> nonBlockablePkgs) + boolean isNonblockable) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn, - checkSaveListener, onSettingsClick, onAppSettingsClick, nonBlockablePkgs, + checkSaveListener, onSettingsClick, onAppSettingsClick, isNonblockable, false /* isBlockingHelper */, false /* isUserSentimentNegative */); } @@ -146,7 +143,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G CheckSaveListener checkSaveListener, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, - Set<String> nonBlockablePkgs, + boolean isNonblockable, boolean isForBlockingHelper, boolean isUserSentimentNegative) throws RemoteException { @@ -162,6 +159,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mSingleNotificationChannel = notificationChannel; mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); mNegativeUserSentiment = isUserSentimentNegative; + mIsNonblockable = isNonblockable; mIsForeground = (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; mIsForBlockingHelper = isForBlockingHelper; @@ -179,22 +177,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G && numTotalChannels == 1; } - try { - final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); - if (Utils.isSystemPackage(getResources(), pm, pkgInfo)) { - if (mSingleNotificationChannel != null - && !mSingleNotificationChannel.isBlockableSystem()) { - mNonblockable = true; - } - } - } catch (PackageManager.NameNotFoundException e) { - // unlikely. - } - if (nonBlockablePkgs != null) { - mNonblockable |= nonBlockablePkgs.contains(pkg); - } - mNonblockable |= (mNumNotificationChannels > 1); - bindHeader(); bindPrompt(); bindButtons(); @@ -261,7 +243,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindPrompt() { final TextView blockPrompt = findViewById(R.id.block_prompt); bindName(); - if (mNonblockable) { + if (mIsNonblockable) { blockPrompt.setText(R.string.notification_unblockable_desc); } else { if (mNegativeUserSentiment) { @@ -288,7 +270,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void saveImportance() { - if (mNonblockable) { + if (mIsNonblockable) { return; } MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, @@ -314,7 +296,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G keep.setOnClickListener(mOnKeepShowing); minimize.setOnClickListener(mOnStopMinNotifications); - if (mNonblockable) { + if (mIsNonblockable) { keep.setText(R.string.notification_done); block.setVisibility(GONE); minimize.setVisibility(GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java new file mode 100644 index 000000000000..56f78f419275 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java @@ -0,0 +1,33 @@ +/* + * 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 com.android.systemui.statusbar; + +import com.android.internal.statusbar.StatusBarIcon; +import java.util.ArrayList; +import java.util.List; + +/** + * Holds an array of {@link com.android.internal.statusbar.StatusBarIcon}s and draws them + * in a linear layout + */ +public class StatusBarIconContainer { + private final List<StatusBarIcon> mIcons = new ArrayList<>(); + + public StatusBarIconContainer(List<StatusBarIcon> icons) { + mIcons.addAll(icons); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 603902a4507d..bd6bd12a5e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -23,6 +25,7 @@ import android.animation.ValueAnimator; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; @@ -56,7 +59,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import java.text.NumberFormat; import java.util.Arrays; -public class StatusBarIconView extends AnimatedImageView { +public class StatusBarIconView extends AnimatedImageView implements StatusIconDisplayable { public static final int NO_COLOR = 0; /** @@ -867,6 +870,21 @@ public class StatusBarIconView extends AnimatedImageView { mOnDismissListener = onDismissListener; } + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + } + + @Override + public boolean isIconVisible() { + return mIcon != null && mIcon.visible; + } + + @Override + public boolean isIconBlocked() { + return mBlocked; + } + public interface OnVisibilityChangedListener { void onVisibilityChanged(int newVisibility); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java new file mode 100644 index 000000000000..b7620f30d742 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.AlphaOptimizedLinearLayout; +import com.android.settingslib.graph.SignalDrawable; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; + +public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements DarkReceiver, + StatusIconDisplayable { + private static final String TAG = "StatusBarMobileView"; + + private String mSlot; + private MobileIconState mState; + private SignalDrawable mMobileDrawable; + private View mInoutContainer; + private ImageView mIn; + private ImageView mOut; + private ImageView mMobile, mMobileType, mMobileRoaming; + private View mMobileRoamingSpace; + + public static StatusBarMobileView fromContext(Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + + return (StatusBarMobileView) + inflater.inflate(R.layout.status_bar_mobile_signal_group, null); + } + + public StatusBarMobileView(Context context) { + super(context); + } + + public StatusBarMobileView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + init(); + } + + private void init() { + mMobile = findViewById(R.id.mobile_signal); + mMobileType = findViewById(R.id.mobile_type); + mMobileRoaming = findViewById(R.id.mobile_roaming); + mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space); + mIn = findViewById(R.id.mobile_in); + mOut = findViewById(R.id.mobile_out); + mInoutContainer = findViewById(R.id.inout_container); + + mMobileDrawable = new SignalDrawable(getContext()); + mMobile.setImageDrawable(mMobileDrawable); + } + + public void applyMobileState(MobileIconState state) { + if (state == null) { + setVisibility(View.GONE); + mState = null; + return; + } + + if (mState == null) { + mState = state; + initViewState(); + return; + } + + if (!mState.equals(state)) { + updateState(state); + } + } + + private void initViewState() { + setContentDescription(mState.contentDescription); + if (!mState.visible) { + setVisibility(View.GONE); + } else { + setVisibility(View.VISIBLE); + } + mMobileDrawable.setLevel(mState.strengthId); + if (mState.typeId > 0) { + mMobileType.setContentDescription(mState.typeContentDescription); + mMobileType.setImageResource(mState.typeId); + mMobileType.setVisibility(View.VISIBLE); + } else { + mMobileType.setVisibility(View.GONE); + } + + mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); + mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility((mState.activityIn || mState.activityOut) + ? View.VISIBLE : View.GONE); + } + + private void updateState(MobileIconState state) { + setContentDescription(state.contentDescription); + if (mState.visible != state.visible) { + setVisibility(state.visible ? View.VISIBLE : View.GONE); + } + if (mState.strengthId != state.strengthId) { + mMobileDrawable.setLevel(state.strengthId); + } + if (mState.typeId != state.typeId && state.typeId != 0) { + mMobileType.setContentDescription(state.typeContentDescription); + mMobileType.setImageResource(state.typeId); + mMobileType.setVisibility(View.VISIBLE); + } else { + mMobileType.setVisibility(View.GONE); + } + + mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE); + mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility((state.activityIn || state.activityOut) + ? View.VISIBLE : View.GONE); + + mState = state; + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mMobileDrawable.setDarkIntensity(darkIntensity); + ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint)); + mIn.setImageTintList(color); + mOut.setImageTintList(color); + mMobileType.setImageTintList(color); + mMobileRoaming.setImageTintList(color); + } + + @Override + public String getSlot() { + return mSlot; + } + + public void setSlot(String slot) { + mSlot = slot; + } + + @Override + public void setStaticDrawableColor(int color) { + ColorStateList list = ColorStateList.valueOf(color); + float intensity = color == Color.WHITE ? 0 : 1; + mMobileDrawable.setDarkIntensity(intensity); + + mIn.setImageTintList(list); + mOut.setImageTintList(list); + mMobileType.setImageTintList(list); + mMobileRoaming.setImageTintList(list); + } + + @Override + public boolean isIconVisible() { + return mState.visible; + } + + @VisibleForTesting + public MobileIconState getState() { + return mState; + } + + @Override + public String toString() { + return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")"; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java new file mode 100644 index 000000000000..afd373ed6321 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; + +import com.android.keyguard.AlphaOptimizedLinearLayout; +import com.android.settingslib.Utils; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; + +/** + * Start small: StatusBarWifiView will be able to layout from a WifiIconState + */ +public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements DarkReceiver, + StatusIconDisplayable { + private static final String TAG = "StatusBarWifiView"; + + private ImageView mWifiIcon; + private ImageView mIn; + private ImageView mOut; + private View mInoutContainer; + private View mSignalSpacer; + private View mAirplaneSpacer; + private WifiIconState mState; + private String mSlot; + private float mDarkIntensity = 0; + + private ContextThemeWrapper mDarkContext; + private ContextThemeWrapper mLightContext; + + public static StatusBarWifiView fromContext(Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + return (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); + } + + public StatusBarWifiView(Context context) { + super(context); + } + + public StatusBarWifiView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + init(); + } + + public void setSlot(String slot) { + mSlot = slot; + } + + @Override + public void setStaticDrawableColor(int color) { + ColorStateList list = ColorStateList.valueOf(color); + mWifiIcon.setImageTintList(list); + mIn.setImageTintList(list); + mOut.setImageTintList(list); + } + + @Override + public String getSlot() { + return mSlot; + } + + @Override + public boolean isIconVisible() { + return mState != null && mState.visible; + } + + private void init() { + int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); + int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); + mLightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); + mDarkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); + + mWifiIcon = findViewById(R.id.wifi_signal); + mIn = findViewById(R.id.wifi_in); + mOut = findViewById(R.id.wifi_out); + mSignalSpacer = findViewById(R.id.wifi_signal_spacer); + mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); + mInoutContainer = findViewById(R.id.inout_container); + } + + public void applyWifiState(WifiIconState state) { + if (state == null) { + setVisibility(View.GONE); + mState = null; + return; + } + + if (mState == null) { + mState = state; + initViewState(); + } + + if (!mState.equals(state)) { + updateState(state); + } + } + + private void updateState(WifiIconState state) { + if (mState.resId != state.resId && state.resId >= 0) { + NeutralGoodDrawable drawable = NeutralGoodDrawable + .create(mLightContext, mDarkContext, state.resId); + drawable.setDarkIntensity(mDarkIntensity); + mWifiIcon.setImageDrawable(drawable); + } + + mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility( + (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE); + mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE); + mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE); + if (mState.visible != state.visible) { + setVisibility(state.visible ? View.VISIBLE : View.GONE); + } + + mState = state; + } + + private void initViewState() { + if (mState.resId >= 0) { + NeutralGoodDrawable drawable = NeutralGoodDrawable.create( + mLightContext, mDarkContext, mState.resId); + drawable.setDarkIntensity(mDarkIntensity); + mWifiIcon.setImageDrawable(drawable); + } + + mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility( + (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); + mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); + mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); + setVisibility(mState.visible ? View.VISIBLE : View.GONE); + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mDarkIntensity = darkIntensity; + Drawable d = mWifiIcon.getDrawable(); + if (d instanceof NeutralGoodDrawable) { + ((NeutralGoodDrawable)d).setDarkIntensity(darkIntensity); + } + mIn.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + mOut.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + } + + + @Override + public String toString() { + return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java new file mode 100644 index 000000000000..ccab0d6fa44b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java @@ -0,0 +1,29 @@ +/* + * 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 com.android.systemui.statusbar; + +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; + +public interface StatusIconDisplayable extends DarkReceiver { + String getSlot(); + void setStaticDrawableColor(int color); + boolean isIconVisible(); + default boolean isIconBlocked() { + return false; + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java index 284113624cc1..b7d501e7f745 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java @@ -46,6 +46,12 @@ public class CarFacetButtonController { } } + public void removeAll() { + mButtonsByCategory.clear(); + mButtonsByPackage.clear(); + mSelectedFacetButton = null; + } + /** * 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 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 a95d0a4dc7b8..3530e0b84664 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.car.hvac.HvacController; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.UserSwitcherController; import java.io.FileDescriptor; @@ -80,12 +81,16 @@ public class CarStatusBar extends StatusBar implements private boolean mShowRight; private boolean mShowBottom; private CarFacetButtonController mCarFacetButtonController; + private ActivityManagerWrapper mActivityManagerWrapper; + private DeviceProvisionedController mDeviceProvisionedController; + private boolean mDeviceIsProvisioned = true; @Override public void start() { super.start(); mTaskStackListener = new TaskStackListenerImpl(); - ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); + mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener); mStackScroller.setScrollingEnabled(true); @@ -96,12 +101,54 @@ public class CarStatusBar extends StatusBar implements Log.d(TAG, "Connecting to HVAC service"); Dependency.get(HvacController.class).connectToCarService(); } + mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); + mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); + if (!mDeviceIsProvisioned) { + mDeviceProvisionedController.addCallback( + new DeviceProvisionedController.DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + mDeviceIsProvisioned = + mDeviceProvisionedController.isDeviceProvisioned(); + restartNavBars(); + } + }); + } + } + + /** + * Remove all content from navbars and rebuild them. Used to allow for different nav bars + * before and after the device is provisioned + */ + private void restartNavBars() { + mCarFacetButtonController.removeAll(); + if (ENABLE_HVAC_CONNECTION) { + Dependency.get(HvacController.class).removeAllComponents(); + } + if (mNavigationBarWindow != null) { + mNavigationBarWindow.removeAllViews(); + mNavigationBarView = null; + } + + if (mLeftNavigationBarWindow != null) { + mLeftNavigationBarWindow.removeAllViews(); + mLeftNavigationBarView = null; + } + + if (mRightNavigationBarWindow != null) { + mRightNavigationBarWindow.removeAllViews(); + mRightNavigationBarView = null; + } + buildNavBarContent(); } + @Override public void destroy() { mCarBatteryController.stopListening(); mConnectedDeviceSignalController.stopListening(); + mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener); if (mNavigationBarWindow != null) { mWindowManager.removeViewImmediate(mNavigationBarWindow); @@ -117,10 +164,10 @@ public class CarStatusBar extends StatusBar implements mWindowManager.removeViewImmediate(mRightNavigationBarWindow); mRightNavigationBarView = null; } - super.destroy(); } + @Override protected void makeStatusBarView() { super.makeStatusBarView(); @@ -167,129 +214,132 @@ public class CarStatusBar extends StatusBar implements @Override protected void createNavigationBar() { - mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); - if (mNavigationBarView != null) { - return; - } - mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar); + mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); + mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); + + buildNavBarWindows(); + buildNavBarContent(); + attachNavBarWindows(); + } + + private void buildNavBarContent() { if (mShowBottom) { - buildBottomBar(); + buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar : + R.layout.car_navigation_bar_unprovisioned); } - int widthForSides = mContext.getResources().getDimensionPixelSize( - R.dimen.navigation_bar_height_car_mode); + if (mShowLeft) { + buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar : + R.layout.car_left_navigation_bar_unprovisioned); + } + if (mShowRight) { + buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar : + R.layout.car_right_navigation_bar_unprovisioned); + } + } - mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); + private void buildNavBarWindows() { + if (mShowBottom) { + mNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); + } if (mShowLeft) { - buildLeft(widthForSides); + mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); + } + if (mShowRight) { + mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); } - mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); + } + + private void attachNavBarWindows() { + if (mShowBottom) { + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + lp.setTitle("CarNavigationBar"); + lp.windowAnimations = 0; + mWindowManager.addView(mNavigationBarWindow, lp); + } + if (mShowLeft) { + int width = mContext.getResources().getDimensionPixelSize( + R.dimen.car_left_navigation_bar_width); + WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( + width, 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); + } if (mShowRight) { - buildRight(widthForSides); + int width = mContext.getResources().getDimensionPixelSize( + R.dimen.car_right_navigation_bar_width); + WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( + width, 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); } } - - private void buildBottomBar() { + private void buildBottomBar(int layout) { // 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. - mNavigationBarWindow = (ViewGroup) View.inflate(mContext, - R.layout.navigation_bar_window, null); - if (mNavigationBarWindow == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window"); - } - - - View.inflate(mContext, R.layout.car_navigation_bar, mNavigationBarWindow); + View.inflate(mContext, layout, mNavigationBarWindow); 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); - - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - lp.setTitle("CarNavigationBar"); - lp.windowAnimations = 0; - - - 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); + private void buildLeft(int layout) { + View.inflate(mContext, layout, 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); - - 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); + private void buildRight(int layout) { + View.inflate(mContext, layout, 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); - - 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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java index 23bf88796da3..7d283d9fde9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java @@ -176,6 +176,14 @@ public class HvacController { }; /** + * Removes all registered components. This is useful if you need to rebuild the UI since + * components self register. + */ + public void removeAllComponents() { + mTempComponents.clear(); + } + + /** * Key for storing {@link TemperatureView}s in a hash map */ private static class HvacKey { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index f42473d4cddf..75b31c5a1edd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -17,8 +17,6 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; -import static com.android.systemui.statusbar.phone.StatusBar.reinflateSignalCluster; - import android.annotation.Nullable; import android.app.Fragment; import android.app.StatusBarManager; @@ -34,7 +32,6 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.EncryptionHelper; @@ -63,7 +60,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private int mDisabled1; private StatusBar mStatusBarComponent; private DarkIconManager mDarkIconManager; - private SignalClusterView mSignalClusterView; private View mOperatorNameFrame; private SignalCallback mSignalCallback = new SignalCallback() { @@ -99,9 +95,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); mClockView = mStatusBar.findViewById(R.id.clock); - mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster); - Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView); - // Default to showing until we know otherwise. showSystemIconArea(false); initEmergencyCryptkeeperText(); initOperatorName(); @@ -128,7 +121,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDestroyView() { super.onDestroyView(); - Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSignalClusterView); Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager); if (mNetworkController.hasEmergencyCryptKeeperText()) { mNetworkController.removeCallback(mSignalCallback); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java index 3f9ae8023149..80c4eb043d24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java @@ -69,7 +69,7 @@ public class DarkIconDispatcherImpl implements DarkIconDispatcher { mReceivers.remove(object); } - public void applyDark(ImageView object) { + public void applyDark(DarkReceiver object) { mReceivers.get(object).onDarkChanged(mTintArea, mDarkIntensity, mIconTint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index edfd02bdfb26..48540b111609 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -20,25 +20,33 @@ import android.graphics.Rect; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; -import com.android.settingslib.Utils; import com.android.systemui.DemoMode; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarMobileView; +import com.android.systemui.statusbar.StatusBarWifiView; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.policy.LocationControllerImpl; -import com.android.systemui.util.leak.LeakDetector; +import java.util.ArrayList; public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver { + private static final String TAG = "DemoStatusIcons"; + private final LinearLayout mStatusIcons; + private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>(); private final int mIconSize; + private StatusBarWifiView mWifiView; private boolean mDemoMode; private int mColor; @@ -56,6 +64,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } public void remove() { + mMobileViews.clear(); ((ViewGroup) getParent()).removeView(this); } @@ -66,7 +75,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private void updateColors() { for (int i = 0; i < getChildCount(); i++) { - StatusBarIconView child = (StatusBarIconView) getChildAt(i); + StatusIconDisplayable child = (StatusIconDisplayable) getChildAt(i); child.setStaticDrawableColor(mColor); } } @@ -145,6 +154,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } } + /// Can only be used to update non-signal related slots private void updateSlot(String slot, String iconPkg, int iconId) { if (!mDemoMode) return; if (iconPkg == null) { @@ -152,7 +162,11 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } int removeIndex = -1; for (int i = 0; i < getChildCount(); i++) { - StatusBarIconView v = (StatusBarIconView) getChildAt(i); + View child = getChildAt(i); + if (!(child instanceof StatusBarIconView)) { + continue; + } + StatusBarIconView v = (StatusBarIconView) child; if (slot.equals(v.getTag())) { if (iconId == 0) { removeIndex = i; @@ -182,8 +196,101 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da addView(v, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize)); } + public void addDemoWifiView(WifiIconState state) { + Log.d(TAG, "addDemoWifiView: "); + StatusBarWifiView view = StatusBarWifiView.fromContext(mContext); + view.setSlot(state.slot); + + int viewIndex = getChildCount(); + // If we have mobile views, put wifi before them + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof StatusBarMobileView) { + viewIndex = i; + break; + } + } + + mWifiView = view; + mWifiView.applyWifiState(state); + mWifiView.setStaticDrawableColor(mColor); + addView(view, viewIndex); + } + + public void updateWifiState(WifiIconState state) { + Log.d(TAG, "updateWifiState: "); + if (mWifiView == null) { + addDemoWifiView(state); + } else { + mWifiView.applyWifiState(state); + } + } + + public void addMobileView(MobileIconState state) { + Log.d(TAG, "addMobileView: "); + StatusBarMobileView view = StatusBarMobileView.fromContext(mContext); + + view.setSlot(state.slot); + view.applyMobileState(state); + view.setStaticDrawableColor(mColor); + + // mobile always goes at the end + mMobileViews.add(view); + addView(view, getChildCount()); + } + + public void updateMobileState(MobileIconState state) { + Log.d(TAG, "updateMobileState: "); + // If the view for this subId exists already, use it + for (int i = 0; i < mMobileViews.size(); i++) { + StatusBarMobileView view = mMobileViews.get(i); + if (view.getState().subId == state.subId) { + view.applyMobileState(state); + return; + } + } + + // Else we have to add it + addMobileView(state); + } + + public void onRemoveIcon(StatusIconDisplayable view) { + if (view.getSlot().equals("wifi")) { + removeView(mWifiView); + mWifiView = null; + } else { + StatusBarMobileView mobileView = matchingMobileView(view); + if (mobileView != null) { + removeView(mobileView); + mMobileViews.remove(mobileView); + } + } + } + + private StatusBarMobileView matchingMobileView(StatusIconDisplayable otherView) { + if (!(otherView instanceof StatusBarMobileView)) { + return null; + } + + StatusBarMobileView v = (StatusBarMobileView) otherView; + for (StatusBarMobileView view : mMobileViews) { + if (view.getState().subId == v.getState().subId) { + return view; + } + } + + return null; + } + @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint)); + + if (mWifiView != null) { + mWifiView.onDarkChanged(area, darkIntensity, tint); + } + for (StatusBarMobileView view : mMobileViews) { + view.onDarkChanged(area, darkIntensity, tint); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 994c0abcf70b..b81780962f08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -207,7 +207,6 @@ public class KeyguardStatusBarView extends RelativeLayout @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { updateLayoutConsideringCutout(); - setSignalClusterLayoutWidth(); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -296,17 +295,6 @@ public class KeyguardStatusBarView extends RelativeLayout return true; } - //TODO: Something is setting signal_cluster to MATCH_PARENT. Why? - private void setSignalClusterLayoutWidth() { - View signalCluster = findViewById(R.id.signal_cluster); - if (signalCluster == null) { - return; - } - - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) signalCluster.getLayoutParams(); - lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; - } - public void setListening(boolean listening) { if (listening == mBatteryListening) { return; @@ -459,7 +447,6 @@ public class KeyguardStatusBarView extends RelativeLayout mIconManager.setTint(iconColor); Rect tintArea = new Rect(0, 0, 0, 0); - applyDarkness(R.id.signal_cluster, tintArea, intensity, iconColor); applyDarkness(R.id.battery, tintArea, intensity, iconColor); applyDarkness(R.id.clock, tintArea, intensity, iconColor); // Reload user avatar diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 33c3ee9b7cba..3e7b0d9f62d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -297,7 +297,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, mContext.getString(R.string.accessibility_location_active)); } else { - mIconController.removeIcon(mSlotLocation); + mIconController.removeAllIconsForSlot(mSlotLocation); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index fb1addfeb1c3..12bdfc674f04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -32,7 +32,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { private final PhoneStatusBarView mView; private final float mIconAlphaWhenOpaque; - private View mLeftSide, mStatusIcons, mSignalCluster, mBattery, mClock; + private View mLeftSide, mStatusIcons, mBattery, mClock; private Animator mCurrentAnimation; public PhoneStatusBarTransitions(PhoneStatusBarView view) { @@ -45,7 +45,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions { public void init() { mLeftSide = mView.findViewById(R.id.notification_icon_area); mStatusIcons = mView.findViewById(R.id.statusIcons); - mSignalCluster = mView.findViewById(R.id.signal_cluster); mBattery = mView.findViewById(R.id.battery); mClock = mView.findViewById(R.id.clock); applyModeBackground(-1, getMode(), false /*animate*/); @@ -90,7 +89,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions { anims.playTogether( animateTransitionTo(mLeftSide, newAlpha), animateTransitionTo(mStatusIcons, newAlpha), - animateTransitionTo(mSignalCluster, newAlpha), animateTransitionTo(mBattery, newAlphaBC), animateTransitionTo(mClock, newAlphaBC) ); @@ -102,7 +100,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions { } else { mLeftSide.setAlpha(newAlpha); mStatusIcons.setAlpha(newAlpha); - mSignalCluster.setAlpha(newAlpha); mBattery.setAlpha(newAlphaBC); mClock.setAlpha(newAlphaBC); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index beeba83b9c25..750d2a5b3d50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -348,6 +348,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected boolean mBouncerShowing; private PhoneStatusBarPolicy mIconPolicy; + private StatusBarSignalPolicy mSignalPolicy; private VolumeComponent mVolumeComponent; private BrightnessMirrorController mBrightnessMirrorController; @@ -747,6 +748,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Lastly, call to the icon policy to install/update all the icons. mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController); + mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController); mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); mUnlockMethodCache.addListener(this); @@ -1131,7 +1133,6 @@ public class StatusBar extends SystemUI implements DemoMode, } protected void reevaluateStyles() { - inflateSignalClusters(); inflateFooterView(); updateFooter(); inflateEmptyShadeView(); @@ -1145,36 +1146,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - private void inflateSignalClusters() { - if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar); - } - - public static SignalClusterView reinflateSignalCluster(View view) { - Context context = view.getContext(); - SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster); - if (signalCluster != null) { - ViewParent parent = signalCluster.getParent(); - if (parent instanceof ViewGroup) { - ViewGroup viewParent = (ViewGroup) parent; - int index = viewParent.indexOfChild(signalCluster); - viewParent.removeView(signalCluster); - SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(context) - .inflate(R.layout.signal_cluster_view, viewParent, false); - ViewGroup.MarginLayoutParams layoutParams = - (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams(); - layoutParams.setMarginsRelative( - context.getResources().getDimensionPixelSize( - R.dimen.signal_cluster_margin_start), - 0, 0, 0); - newCluster.setLayoutParams(layoutParams); - viewParent.addView(newCluster, index); - return newCluster; - } - return signalCluster; - } - return null; - } - private void inflateEmptyShadeView() { if (mStackScroller == null) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 956bebb6ca41..94e004bd1b47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -16,12 +16,16 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; import static android.app.StatusBarManager.DISABLE_NONE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import android.content.Context; import android.os.Bundle; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArraySet; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -33,19 +37,39 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarMobileView; +import com.android.systemui.statusbar.StatusBarWifiView; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.util.Utils.DisableStateTracker; +import java.util.List; public interface StatusBarIconController { + /** + * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon + * in that slot and not added as a sub slot. + */ + public static final int TAG_PRIMARY = 0; + public void addIconGroup(IconManager iconManager); public void removeIconGroup(IconManager iconManager); public void setExternalIcon(String slot); public void setIcon(String slot, int resourceId, CharSequence contentDescription); public void setIcon(String slot, StatusBarIcon icon); - public void setIconVisibility(String slotTty, boolean b); - public void removeIcon(String slot); + public void setSignalIcon(String slot, WifiIconState state); + public void setMobileIcons(String slot, List<MobileIconState> states); + public void setIconVisibility(String slot, boolean b); + /** + * If you don't know what to pass for `tag`, either remove all icons for slot, or use + * TAG_PRIMARY to refer to the first icon at a given slot. + */ + public void removeIcon(String slot, int tag); + public void removeAllIconsForSlot(String slot); public static final String ICON_BLACKLIST = "icon_blacklist"; @@ -79,9 +103,9 @@ public interface StatusBarIconController { @Override protected void onIconAdded(int index, String slot, boolean blocked, - StatusBarIcon icon) { - StatusBarIconView v = addIcon(index, slot, blocked, icon); - mDarkIconDispatcher.addDarkReceiver(v); + StatusBarIconHolder holder) { + StatusIconDisplayable view = addHolder(index, slot, blocked, holder); + mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view); } @Override @@ -95,21 +119,21 @@ public interface StatusBarIconController { @Override protected void destroy() { for (int i = 0; i < mGroup.getChildCount(); i++) { - mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(i)); + mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i)); } mGroup.removeAllViews(); } @Override protected void onRemoveIcon(int viewIndex) { - mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(viewIndex)); + mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex)); super.onRemoveIcon(viewIndex); } @Override public void onSetIcon(int viewIndex, StatusBarIcon icon) { super.onSetIcon(viewIndex, icon); - mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex)); + mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex)); } @Override @@ -135,17 +159,18 @@ public interface StatusBarIconController { } @Override - protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIcon icon) { - StatusBarIconView v = addIcon(index, slot, blocked, icon); - v.setStaticDrawableColor(mColor); + protected void onIconAdded(int index, String slot, boolean blocked, + StatusBarIconHolder holder) { + StatusIconDisplayable view = addHolder(index, slot, blocked, holder); + view.setStaticDrawableColor(mColor); } public void setTint(int color) { mColor = color; for (int i = 0; i < mGroup.getChildCount(); i++) { View child = mGroup.getChildAt(i); - if (child instanceof StatusBarIconView) { - StatusBarIconView icon = (StatusBarIconView) child; + if (child instanceof StatusIconDisplayable) { + StatusIconDisplayable icon = (StatusIconDisplayable) child; icon.setStaticDrawableColor(mColor); } } @@ -171,6 +196,7 @@ public interface StatusBarIconController { // Enables SystemUI demo mode to take effect in this group protected boolean mDemoable = true; + private boolean mIsInDemoMode; protected DemoStatusIcons mDemoStatusIcons; public IconManager(ViewGroup group) { @@ -205,10 +231,27 @@ public interface StatusBarIconController { } protected void onIconAdded(int index, String slot, boolean blocked, - StatusBarIcon icon) { - addIcon(index, slot, blocked, icon); + StatusBarIconHolder holder) { + addHolder(index, slot, blocked, holder); + } + + protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked, + StatusBarIconHolder holder) { + switch (holder.getType()) { + case TYPE_ICON: + return addIcon(index, slot, blocked, holder.getIcon()); + + case TYPE_WIFI: + return addSignalIcon(index, slot, holder.getWifiState()); + + case TYPE_MOBILE: + return addMobileIcon(index, slot, holder.getMobileState()); + } + + return null; } + @VisibleForTesting protected StatusBarIconView addIcon(int index, String slot, boolean blocked, StatusBarIcon icon) { StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); @@ -218,10 +261,45 @@ public interface StatusBarIconController { } @VisibleForTesting - protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { + protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { + StatusBarWifiView view = onCreateStatusBarWifiView(slot); + view.applyWifiState(state); + mGroup.addView(view, index, onCreateLayoutParams()); + + if (mIsInDemoMode) { + mDemoStatusIcons.addDemoWifiView(state); + } + return view; + } + + @VisibleForTesting + protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { + StatusBarMobileView view = onCreateStatusBarMobileView(slot); + view.applyMobileState(state); + mGroup.addView(view, index, onCreateLayoutParams()); + + if (mIsInDemoMode) { + mDemoStatusIcons.addMobileView(state); + } + return view; + } + + private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { return new StatusBarIconView(mContext, slot, null, blocked); } + private StatusBarWifiView onCreateStatusBarWifiView(String slot) { + StatusBarWifiView view = StatusBarWifiView.fromContext(mContext); + view.setSlot(slot); + return view; + } + + private StatusBarMobileView onCreateStatusBarMobileView(String slot) { + StatusBarMobileView view = StatusBarMobileView.fromContext(mContext); + view.setSlot(slot); + return view; + } + protected LinearLayout.LayoutParams onCreateLayoutParams() { return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); } @@ -256,6 +334,9 @@ public interface StatusBarIconController { } protected void onRemoveIcon(int viewIndex) { + if (mIsInDemoMode) { + mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex)); + } mGroup.removeViewAt(viewIndex); } @@ -264,17 +345,59 @@ public interface StatusBarIconController { view.set(icon); } + public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { + switch (holder.getType()) { + case TYPE_ICON: + onSetIcon(viewIndex, holder.getIcon()); + return; + case TYPE_WIFI: + onSetSignalIcon(viewIndex, holder.getWifiState()); + return; + + case TYPE_MOBILE: + onSetMobileIcon(viewIndex, holder.getMobileState()); + default: + break; + } + } + + public void onSetSignalIcon(int viewIndex, WifiIconState state) { + StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex); + if (wifiView != null) { + wifiView.applyWifiState(state); + } + + if (mIsInDemoMode) { + mDemoStatusIcons.updateWifiState(state); + } + } + + public void onSetMobileIcon(int viewIndex, MobileIconState state) { + StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex); + if (view != null) { + view.applyMobileState(state); + } + + if (mIsInDemoMode) { + mDemoStatusIcons.updateMobileState(state); + } + } + @Override public void dispatchDemoCommand(String command, Bundle args) { if (!mDemoable) { return; } - if (mDemoStatusIcons != null && command.equals(COMMAND_EXIT)) { - mDemoStatusIcons.dispatchDemoCommand(command, args); - exitDemoMode(); + if (command.equals(COMMAND_EXIT)) { + if (mDemoStatusIcons != null) { + mDemoStatusIcons.dispatchDemoCommand(command, args); + exitDemoMode(); + } + mIsInDemoMode = false; } else { if (mDemoStatusIcons == null) { + mIsInDemoMode = true; mDemoStatusIcons = createDemoStatusIcons(); } mDemoStatusIcons.dispatchDemoCommand(command, args); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 8f5e705ff2a4..510af03e6f28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -16,13 +16,14 @@ package com.android.systemui.statusbar.phone; +import android.annotation.NonNull; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; +import android.util.ArrayMap; import android.util.ArraySet; import android.view.ViewGroup; -import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Dependency; @@ -30,7 +31,9 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusIconDisplayable; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.IconLogger; @@ -40,8 +43,9 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; -import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.STATUS_BAR_ICON_MANAGER_TAG; +import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; /** * Receives the callbacks from CommandQueue related to icons and tracks the state of @@ -50,20 +54,25 @@ import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.ST */ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController { + private static final String TAG = "StatusBarIconController"; private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); private final ArraySet<String> mIconBlacklist = new ArraySet<>(); private final IconLogger mIconLogger = Dependency.get(IconLogger.class); + // Points to light or dark context depending on the... context? private Context mContext; - private DemoStatusIcons mDemoStatusIcons; - private IconManager mStatusBarIconManager; + private Context mLightContext; + private Context mDarkContext; + + private boolean mIsDark = false; public StatusBarIconControllerImpl(Context context) { super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons)); Dependency.get(ConfigurationController.class).addCallback(this); + mContext = context; loadDimens(); @@ -76,12 +85,16 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu @Override public void addIconGroup(IconManager group) { mIconGroups.add(group); - for (int i = 0; i < mIcons.size(); i++) { - StatusBarIcon icon = mIcons.get(i); - if (icon != null) { - String slot = mSlots.get(i); - boolean blocked = mIconBlacklist.contains(slot); - group.onIconAdded(getViewIndex(getSlotIndex(slot)), slot, blocked, icon); + List<Slot> allSlots = getSlots(); + for (int i = 0; i < allSlots.size(); i++) { + Slot slot = allSlots.get(i); + List<StatusBarIconHolder> holders = slot.getHolderList(); + boolean blocked = mIconBlacklist.contains(slot.getName()); + + for (StatusBarIconHolder holder : holders) { + int tag = holder.getTag(); + int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag()); + group.onIconAdded(viewIndex, slot.getName(), blocked, holder); } } } @@ -99,104 +112,209 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } mIconBlacklist.clear(); mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue)); - ArrayList<StatusBarIcon> current = new ArrayList<>(mIcons); - ArrayList<String> currentSlots = new ArrayList<>(mSlots); + ArrayList<Slot> currentSlots = getSlots(); + ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>(); + + // This is a little hacky... Peel off all of the holders on all of the slots + // but keep them around so they can be re-added + // Remove all the icons. - for (int i = current.size() - 1; i >= 0; i--) { - removeIcon(currentSlots.get(i)); + for (int i = currentSlots.size() - 1; i >= 0; i--) { + Slot s = currentSlots.get(i); + slotsToReAdd.put(s, s.getHolderList()); + removeAllIconsForSlot(s.getName()); } + // Add them all back - for (int i = 0; i < current.size(); i++) { - setIcon(currentSlots.get(i), current.get(i)); + for (int i = 0; i < currentSlots.size(); i++) { + Slot item = currentSlots.get(i); + List<StatusBarIconHolder> iconsForSlot = slotsToReAdd.get(item); + if (iconsForSlot == null) continue; + for (StatusBarIconHolder holder : iconsForSlot) { + setIcon(getSlotIndex(item.getName()), holder); + } } } private void loadDimens() { } - private void addSystemIcon(int index, StatusBarIcon icon) { - String slot = getSlot(index); - int viewIndex = getViewIndex(index); + private void addSystemIcon(int index, StatusBarIconHolder holder) { + String slot = getSlotName(index); + int viewIndex = getViewIndex(index, holder.getTag()); boolean blocked = mIconBlacklist.contains(slot); - mIconLogger.onIconVisibility(getSlot(index), icon.visible); - mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon)); + mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible()); + mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder)); } @Override public void setIcon(String slot, int resourceId, CharSequence contentDescription) { int index = getSlotIndex(slot); - StatusBarIcon icon = getIcon(index); - if (icon == null) { - icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), - Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription); - setIcon(slot, icon); + StatusBarIconHolder holder = getIcon(index, 0); + if (holder == null) { + StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), + Icon.createWithResource( + mContext, resourceId), 0, 0, contentDescription); + holder = StatusBarIconHolder.fromIcon(icon); + setIcon(index, holder); } else { - icon.icon = Icon.createWithResource(mContext, resourceId); - icon.contentDescription = contentDescription; - handleSet(index, icon); + holder.getIcon().icon = Icon.createWithResource(mContext, resourceId); + holder.getIcon().contentDescription = contentDescription; + handleSet(index, holder); + } + } + + /** + * Signal icons need to be handled differently, because they can be + * composite views + */ + @Override + public void setSignalIcon(String slot, WifiIconState state) { + + int index = getSlotIndex(slot); + + if (state == null) { + removeIcon(index, 0); + return; + } + + StatusBarIconHolder holder = getIcon(index, 0); + if (holder == null) { + holder = StatusBarIconHolder.fromWifiIconState(state); + setIcon(index, holder); + } else { + holder.setWifiState(state); + handleSet(index, holder); + } + } + + /** + * Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted + * by subId. Don't worry this definitely makes sense and works. + * @param slot da slot + * @param iconStates All of the mobile icon states + */ + @Override + public void setMobileIcons(String slot, List<MobileIconState> iconStates) { + Slot mobileSlot = getSlot(slot); + int slotIndex = getSlotIndex(slot); + + for (MobileIconState state : iconStates) { + StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId); + if (holder == null) { + holder = StatusBarIconHolder.fromMobileIconState(state); + setIcon(slotIndex, holder); + } else { + holder.setMobileState(state); + handleSet(slotIndex, holder); + } } } @Override public void setExternalIcon(String slot) { - int viewIndex = getViewIndex(getSlotIndex(slot)); + int viewIndex = getViewIndex(getSlotIndex(slot), 0); int height = mContext.getResources().getDimensionPixelSize( R.dimen.status_bar_icon_drawing_size); mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height)); } + //TODO: remove this (used in command queue and for 3rd party tiles?) @Override public void setIcon(String slot, StatusBarIcon icon) { setIcon(getSlotIndex(slot), icon); } + /** + * For backwards compatibility, in the event that someone gives us a slot and a status bar icon + */ + private void setIcon(int index, StatusBarIcon icon) { + if (icon == null) { + removeAllIconsForSlot(getSlotName(index)); + return; + } + + StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon); + setIcon(index, holder); + } + @Override - public void removeIcon(String slot) { - int index = getSlotIndex(slot); - removeIcon(index); + public void setIcon(int index, @NonNull StatusBarIconHolder holder) { + boolean isNew = getIcon(index, holder.getTag()) == null; + super.setIcon(index, holder); + + if (isNew) { + addSystemIcon(index, holder); + } else { + handleSet(index, holder); + } } public void setIconVisibility(String slot, boolean visibility) { int index = getSlotIndex(slot); - StatusBarIcon icon = getIcon(index); - if (icon == null || icon.visible == visibility) { + StatusBarIconHolder holder = getIcon(index, 0); + if (holder == null || holder.isVisible() == visibility) { return; } - icon.visible = visibility; - handleSet(index, icon); + + holder.setVisible(visibility); + handleSet(index, holder); + } + + public void removeIcon(String slot) { + removeAllIconsForSlot(slot); + } + + @Override + public void removeIcon(String slot, int tag) { + removeIcon(getSlotIndex(slot), tag); } @Override - public void removeIcon(int index) { - if (getIcon(index) == null) { + public void removeAllIconsForSlot(String slotName) { + Slot slot = getSlot(slotName); + if (!slot.hasIconsInSlot()) { return; } - mIconLogger.onIconHidden(getSlot(index)); - super.removeIcon(index); - int viewIndex = getViewIndex(index); - mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); + + mIconLogger.onIconHidden(slotName); + + int slotIndex = getSlotIndex(slotName); + List<StatusBarIconHolder> iconsToRemove = slot.getHolderList(); + for (StatusBarIconHolder holder : iconsToRemove) { + int viewIndex = getViewIndex(slotIndex, holder.getTag()); + slot.removeForTag(holder.getTag()); + mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); + } } @Override - public void setIcon(int index, StatusBarIcon icon) { - if (icon == null) { - removeIcon(index); + public void removeIcon(int index, int tag) { + if (getIcon(index, tag) == null) { return; } - boolean isNew = getIcon(index) == null; - super.setIcon(index, icon); - if (isNew) { - addSystemIcon(index, icon); - } else { - handleSet(index, icon); - } + mIconLogger.onIconHidden(getSlotName(index)); + super.removeIcon(index, tag); + int viewIndex = getViewIndex(index, 0); + mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); } - private void handleSet(int index, StatusBarIcon icon) { - int viewIndex = getViewIndex(index); - mIconLogger.onIconVisibility(getSlot(index), icon.visible); - mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon)); + private void handleSet(int index, StatusBarIconHolder holder) { + int viewIndex = getViewIndex(index, holder.getTag()); + mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible()); + mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); + } + + /** + * For mobile essentially (an array of holders in one slot) + */ + private void handleSet(int slotIndex, List<StatusBarIconHolder> holders) { + for (StatusBarIconHolder holder : holders) { + int viewIndex = getViewIndex(slotIndex, holder.getTag()); + mIconLogger.onIconVisibility(getSlotName(slotIndex), holder.isVisible()); + mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); + } } @Override @@ -208,7 +326,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu int N = group.getChildCount(); pw.println(" icon views: " + N); for (int i = 0; i < N; i++) { - StatusBarIconView ic = (StatusBarIconView) group.getChildAt(i); + StatusIconDisplayable ic = (StatusIconDisplayable) group.getChildAt(i); pw.println(" [" + i + "] icon=" + ic); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java new file mode 100644 index 000000000000..e854dd0cd737 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -0,0 +1,131 @@ +/* + * 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 com.android.systemui.statusbar.phone; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.os.UserHandle; +import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; + +/** + * Wraps {@link com.android.internal.statusbar.StatusBarIcon} so we can still have a uniform list + */ +public class StatusBarIconHolder { + public static final int TYPE_ICON = 0; + public static final int TYPE_WIFI = 1; + public static final int TYPE_MOBILE = 2; + + private StatusBarIcon mIcon; + private WifiIconState mWifiState; + private MobileIconState mMobileState; + private int mType = TYPE_ICON; + private int mTag = 0; + private boolean mVisible = true; + + public static StatusBarIconHolder fromIcon(StatusBarIcon icon) { + StatusBarIconHolder wrapper = new StatusBarIconHolder(); + wrapper.mIcon = icon; + + return wrapper; + } + + public static StatusBarIconHolder fromResId(Context context, int resId, + CharSequence contentDescription) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), + Icon.createWithResource( context, resId), 0, 0, contentDescription); + return holder; + } + + public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mWifiState = state; + holder.mType = TYPE_WIFI; + return holder; + } + + public static StatusBarIconHolder fromMobileIconState(MobileIconState state) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mMobileState = state; + holder.mType = TYPE_MOBILE; + holder.mTag = state.subId; + return holder; + } + + public int getType() { + return mType; + } + + @Nullable + public StatusBarIcon getIcon() { + return mIcon; + } + + @Nullable + public WifiIconState getWifiState() { + return mWifiState; + } + + public void setWifiState(WifiIconState state) { + mWifiState = state; + } + + @Nullable + public MobileIconState getMobileState() { + return mMobileState; + } + + public void setMobileState(MobileIconState state) { + mMobileState = state; + } + + public boolean isVisible() { + switch (mType) { + case TYPE_ICON: + return mIcon.visible; + case TYPE_WIFI: + return mWifiState.visible; + case TYPE_MOBILE: + return mMobileState.visible; + + default: return true; + } + } + + public void setVisible(boolean visible) { + switch (mType) { + case TYPE_ICON: + mIcon.visible = visible; + break; + + case TYPE_WIFI: + mWifiState.visible = visible; + break; + + case TYPE_MOBILE: + mMobileState.visible = visible; + break; + } + } + + public int getTag() { + return mTag; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java index 1aa3a4312f8d..c773170a2756 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java @@ -16,64 +16,78 @@ package com.android.systemui.statusbar.phone; +import android.annotation.NonNull; +import android.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; - import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; + +import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; public class StatusBarIconList { - protected ArrayList<String> mSlots = new ArrayList<>(); - protected ArrayList<StatusBarIcon> mIcons = new ArrayList<>(); + private ArrayList<Slot> mSlots = new ArrayList<>(); public StatusBarIconList(String[] slots) { final int N = slots.length; for (int i=0; i < N; i++) { - mSlots.add(slots[i]); - mIcons.add(null); + mSlots.add(new Slot(slots[i], null)); } } public int getSlotIndex(String slot) { final int N = mSlots.size(); for (int i=0; i<N; i++) { - if (slot.equals(mSlots.get(i))) { + Slot item = mSlots.get(i); + if (item.getName().equals(slot)) { return i; } } // Auto insert new items at the beginning. - mSlots.add(0, slot); - mIcons.add(0, null); + mSlots.add(0, new Slot(slot, null)); return 0; } + protected ArrayList<Slot> getSlots() { + return new ArrayList<>(mSlots); + } + + protected Slot getSlot(String name) { + return mSlots.get(getSlotIndex(name)); + } + public int size() { return mSlots.size(); } - public void setIcon(int index, StatusBarIcon icon) { - mIcons.set(index, icon); + public void setIcon(int index, @NonNull StatusBarIconHolder holder) { + mSlots.get(index).addHolder(holder); } - public void removeIcon(int index) { - mIcons.set(index, null); + public void removeIcon(int index, int tag) { + mSlots.get(index).removeForTag(tag); } - public String getSlot(int index) { - return mSlots.get(index); + public String getSlotName(int index) { + return mSlots.get(index).getName(); } - public StatusBarIcon getIcon(int index) { - return mIcons.get(index); + public StatusBarIconHolder getIcon(int index, int tag) { + return mSlots.get(index).getHolderForTag(tag); } - public int getViewIndex(int index) { + public int getViewIndex(int slotIndex, int tag) { int count = 0; - for (int i = 0; i < index; i++) { - if (mIcons.get(i) != null) { - count++; + for (int i = 0; i < slotIndex; i++) { + Slot item = mSlots.get(i); + if (item.hasIconsInSlot()) { + count += item.numberOfIcons(); } } - return count; + + Slot viewItem = mSlots.get(slotIndex); + return count + viewItem.viewIndexOffsetForTag(tag); } public void dump(PrintWriter pw) { @@ -81,7 +95,163 @@ public class StatusBarIconList { final int N = mSlots.size(); pw.println(" icon slots: " + N); for (int i=0; i<N; i++) { - pw.printf(" %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i)); + pw.printf(" %2d:%s\n", i, mSlots.get(i).toString()); + } + } + + public static class Slot { + private final String mName; + private StatusBarIconHolder mHolder; + /** + * Only used if multiple icons are added to the same slot. + * + * If there are mSubSlots, then these are structured like: + * [ First item | (the rest) ] + * + * The tricky thing to keep in mind here is that the list [mHolder, mSubSlots] is ordered + * ascending, but for view logic we should go backwards through the list. I.e., the first + * element (mHolder) should be the highest index, because higher priority items go to the + * right of lower priority items + */ + private ArrayList<StatusBarIconHolder> mSubSlots; + + public Slot(String name, StatusBarIconHolder iconHolder) { + mName = name; + mHolder = iconHolder; + } + + public String getName() { + return mName; + } + + @Nullable + public StatusBarIconHolder getHolderForTag(int tag) { + if (tag == TAG_PRIMARY) { + return mHolder; + } + + if (mSubSlots != null) { + for (StatusBarIconHolder holder : mSubSlots) { + if (holder.getTag() == tag) { + return holder; + } + } + } + + return null; + } + + public void addHolder(StatusBarIconHolder holder) { + int tag = holder.getTag(); + if (tag == TAG_PRIMARY) { + mHolder = holder; + } else { + setSubSlot(holder, tag); + } + } + + public void removeForTag(int tag) { + if (tag == TAG_PRIMARY) { + mHolder = null; + } else { + int index = getIndexForTag(tag); + if (index != -1) { + mSubSlots.remove(index); + } + } + } + + @VisibleForTesting + public void clear() { + mHolder = null; + if (mSubSlots != null) { + mSubSlots = null; + } + } + + private void setSubSlot(StatusBarIconHolder holder, int tag) { + if (mSubSlots == null) { + mSubSlots = new ArrayList<>(); + mSubSlots.add(holder); + return; + } + + if (getIndexForTag(tag) != -1) { + // Holder exists for tag; no-op + return; + } + + // These holders get added to the end. Confused yet? + mSubSlots.add(holder); + } + + private int getIndexForTag(int tag) { + for (int i = 0; i < mSubSlots.size(); i++) { + StatusBarIconHolder h = mSubSlots.get(i); + if (h.getTag() == tag) { + return i; + } + } + + return -1; + } + + public boolean hasIconsInSlot() { + if (mHolder != null) return true; + if (mSubSlots == null) return false; + + return mSubSlots.size() > 0; + } + + public int numberOfIcons() { + int num = mHolder == null ? 0 : 1; + if (mSubSlots == null) return num; + + return num + mSubSlots.size(); + } + + /** + * View index is backwards from regular index + * @param tag the tag of the holder being viewed + * @return (1 + mSubSlots.size() - indexOfTag) + */ + public int viewIndexOffsetForTag(int tag) { + if (mSubSlots == null) { + return 0; + } + + int subSlots = mSubSlots.size(); + if (tag == TAG_PRIMARY) { + return subSlots; + } + + return subSlots - getIndexForTag(tag) - 1; + } + + public List<StatusBarIconHolder> getHolderList() { + ArrayList<StatusBarIconHolder> holders = new ArrayList<>(); + if (mHolder != null) { + holders.add(mHolder); + } + + if (mSubSlots != null) { + holders.addAll(mSubSlots); + } + + return holders; + } + + @Override + public String toString() { + return String.format("(%s) %s", mName, subSlotsString()); + } + + private String subSlotsString() { + if (mSubSlots == null) { + return ""; + } + + return "" + mSubSlots.size() + " subSlots"; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java new file mode 100644 index 000000000000..c5a3a0d33cd3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.telephony.SubscriptionInfo; +import android.util.ArraySet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageView; +import com.android.settingslib.graph.SignalDrawable; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.policy.SecurityController; +import com.android.systemui.tuner.TunerService.Tunable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback, + SecurityController.SecurityControllerCallback, Tunable { + private static final String TAG = "StatusBarSignalPolicy"; + + private final String mSlotAirplane; + private final String mSlotMobile; + private final String mSlotWifi; + private final String mSlotEthernet; + private final String mSlotVpn; + + private final Context mContext; + private final StatusBarIconController mIconController; + private final NetworkController mNetworkController; + private final SecurityController mSecurityController; + private final Handler mHandler = Handler.getMain(); + + private boolean mBlockAirplane; + private boolean mBlockMobile; + private boolean mBlockWifi; + private boolean mBlockEthernet; + private boolean mActivityEnabled; + private boolean mForceBlockWifi; + + // Track as little state as possible, and only for padding purposes + private boolean mIsAirplaneMode = false; + private boolean mWifiVisible = false; + + private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>(); + private WifiIconState mWifiIconState = new WifiIconState(); + + public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) { + mContext = context; + + mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); + mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); + mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); + mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); + mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); + + mIconController = iconController; + mNetworkController = Dependency.get(NetworkController.class); + mSecurityController = Dependency.get(SecurityController.class); + + mNetworkController.addCallback(this); + mSecurityController.addCallback(this); + } + + public void destroy() { + mNetworkController.removeCallback(this); + mSecurityController.removeCallback(this); + } + + private void updateVpn() { + boolean vpnVisible = mSecurityController.isVpnEnabled(); + int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); + + mIconController.setIcon(mSlotVpn, vpnIconId, null); + mIconController.setIconVisibility(mSlotVpn, vpnVisible); + } + + private int currentVpnIconId(boolean isBranded) { + return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; + } + + private void updateActivityEnabled() { + mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); + } + + /** + * From SecurityController + */ + @Override + public void onStateChanged() { + mHandler.post(this::updateVpn); + } + + @Override + public void onTuningChanged(String key, String newValue) { + if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { + return; + } + ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue); + boolean blockAirplane = blockList.contains(mSlotAirplane); + boolean blockMobile = blockList.contains(mSlotMobile); + boolean blockWifi = blockList.contains(mSlotWifi); + boolean blockEthernet = blockList.contains(mSlotEthernet); + + if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile + || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) { + mBlockAirplane = blockAirplane; + mBlockMobile = blockMobile; + mBlockEthernet = blockEthernet; + mBlockWifi = blockWifi || mForceBlockWifi; + // Re-register to get new callbacks. + mNetworkController.removeCallback(this); + } + } + + @Override + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description, boolean isTransient, + String statusLabel) { + + boolean visible = statusIcon.visible && !mBlockWifi; + boolean in = activityIn && mActivityEnabled && visible; + boolean out = activityOut && mActivityEnabled && visible; + + mWifiIconState.visible = visible; + mWifiIconState.resId = statusIcon.icon; + mWifiIconState.activityIn = in; + mWifiIconState.activityOut = out; + mWifiIconState.slot = mSlotWifi; + mWifiIconState.airplaneSpacerVisible = mIsAirplaneMode; + mWifiIconState.contentDescription = statusIcon.contentDescription; + + if (mWifiIconState.visible && mWifiIconState.resId > 0) { + mIconController.setSignalIcon(mSlotWifi, mWifiIconState.copy()); + mIconController.setIconVisibility(mSlotWifi, true); + } else { + mIconController.setIconVisibility(mSlotWifi, false); + } + } + + @Override + public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, + int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, + String description, boolean isWide, int subId, boolean roaming) { + MobileIconState state = getState(subId); + if (state == null) { + return; + } + + state.visible = statusIcon.visible && !mBlockMobile; + state.strengthId = statusIcon.icon; + state.typeId = statusType; + state.contentDescription = statusIcon.contentDescription; + state.typeContentDescription = typeContentDescription; + state.roaming = roaming; + state.activityIn = activityIn && mActivityEnabled; + state.activityOut = activityOut && mActivityEnabled; + + // Always send a copy to maintain value type semantics + mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); + } + + private MobileIconState getState(int subId) { + for (MobileIconState state : mMobileStates) { + if (state.subId == subId) { + return state; + } + } + Log.e(TAG, "Unexpected subscription " + subId); + return null; + } + + + /** + * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators + * so we don't have to update the icon manager at this point, just remove the old ones + * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8) + */ + @Override + public void setSubs(List<SubscriptionInfo> subs) { + if (hasCorrectSubs(subs)) { + return; + } + + mIconController.removeAllIconsForSlot(mSlotMobile); + mMobileStates.clear(); + final int n = subs.size(); + for (int i = 0; i < n; i++) { + mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId())); + } + } + + private boolean hasCorrectSubs(List<SubscriptionInfo> subs) { + final int N = subs.size(); + if (N != mMobileStates.size()) { + return false; + } + for (int i = 0; i < N; i++) { + if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) { + return false; + } + } + return true; + } + + @Override + public void setNoSims(boolean show, boolean simDetected) { + // Noop yay! + } + + + @Override + public void setEthernetIndicators(IconState state) { + boolean visible = state.visible && !mBlockEthernet; + int resId = state.icon; + String description = state.contentDescription; + + if (resId > 0) { + mIconController.setIcon(mSlotEthernet, resId, description); + mIconController.setIconVisibility(mSlotEthernet, true); + } else { + mIconController.setIconVisibility(mSlotEthernet, false); + } + } + + @Override + public void setIsAirplaneMode(IconState icon) { + mIsAirplaneMode = icon.visible && !mBlockAirplane; + int resId = icon.icon; + String description = icon.contentDescription; + + if (mIsAirplaneMode && resId > 0) { + mIconController.setIcon(mSlotAirplane, resId, description); + mIconController.setIconVisibility(mSlotAirplane, true); + } else { + mIconController.setIconVisibility(mSlotAirplane, false); + } + } + + @Override + public void setMobileDataEnabled(boolean enabled) { + // Don't care. + } + + private static abstract class SignalIconState { + public boolean visible; + public boolean activityOut; + public boolean activityIn; + public String slot; + public String contentDescription; + + @Override + public boolean equals(Object o) { + // Skipping reference equality bc this should be more of a value type + if (o == null || getClass() != o.getClass()) { + return false; + } + SignalIconState that = (SignalIconState) o; + return visible == that.visible && + activityOut == that.activityOut && + activityIn == that.activityIn && + Objects.equals(contentDescription, that.contentDescription) && + Objects.equals(slot, that.slot); + } + + @Override + public int hashCode() { + return Objects.hash(visible, activityOut, slot); + } + + protected void copyTo(SignalIconState other) { + other.visible = visible; + other.activityIn = activityIn; + other.activityOut = activityOut; + other.slot = slot; + other.contentDescription = contentDescription; + } + } + + public static class WifiIconState extends SignalIconState{ + public int resId; + public boolean airplaneSpacerVisible; + public boolean signalSpacerVisible; + + @Override + public boolean equals(Object o) { + // Skipping reference equality bc this should be more of a value type + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + WifiIconState that = (WifiIconState) o; + return resId == that.resId && + airplaneSpacerVisible == that.airplaneSpacerVisible && + signalSpacerVisible == that.signalSpacerVisible; + } + + public void copyTo(WifiIconState other) { + super.copyTo(other); + other.resId = resId; + other.airplaneSpacerVisible = airplaneSpacerVisible; + other.signalSpacerVisible = signalSpacerVisible; + } + + public WifiIconState copy() { + WifiIconState newState = new WifiIconState(); + copyTo(newState); + return newState; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), + resId, airplaneSpacerVisible, signalSpacerVisible); + } + + @Override public String toString() { + return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; + } + } + + /** + * A little different. This one delegates to SignalDrawable instead of a specific resId + */ + public static class MobileIconState extends SignalIconState { + public int subId; + public int strengthId; + public int typeId; + public boolean roaming; + public boolean needsLeadingPadding; + public String typeContentDescription; + + private MobileIconState(int subId) { + super(); + this.subId = subId; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + MobileIconState that = (MobileIconState) o; + return subId == that.subId && + strengthId == that.strengthId && + typeId == that.typeId && + roaming == that.roaming && + needsLeadingPadding == that.needsLeadingPadding && + Objects.equals(typeContentDescription, that.typeContentDescription); + } + + @Override + public int hashCode() { + + return Objects + .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding, + typeContentDescription); + } + + public void copyTo(MobileIconState other) { + super.copyTo(other); + other.subId = subId; + other.strengthId = strengthId; + other.typeId = typeId; + other.roaming = roaming; + other.needsLeadingPadding = needsLeadingPadding; + other.typeContentDescription = typeContentDescription; + } + + private static List<MobileIconState> copyStates(List<MobileIconState> inStates) { + ArrayList<MobileIconState> outStates = new ArrayList<>(); + for (MobileIconState state : inStates) { + MobileIconState copy = new MobileIconState(state.subId); + state.copyTo(copy); + outStates.add(copy); + } + + return outStates; + } + + @Override public String toString() { + return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming=" + + roaming + ", visible=" + visible + ")"; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index dab28d65a5a9..255e10e2680d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -18,13 +18,13 @@ package com.android.systemui.statusbar.phone; import android.annotation.Nullable; import android.content.Context; -import android.util.ArrayMap; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.stack.ViewState; /** @@ -97,7 +97,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * Layout is happening from end -> start */ private void calculateIconTranslations() { - float width = getWidth(); + float width = getWidth() - getPaddingEnd(); float translationX = width; float contentStart = getPaddingStart(); int childCount = getChildCount(); @@ -109,18 +109,22 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { //TODO: Dots for (int i = childCount - 1; i >= 0; i--) { View child = getChildAt(i); - if (!(child instanceof StatusBarIconView)) { + if (!(child instanceof StatusIconDisplayable)) { + if (DEBUG) Log.d(TAG, "skipping child (wrong type)"); continue; } + StatusIconDisplayable iconView = (StatusIconDisplayable) child; + ViewState childState = getViewStateFromChild(child); if (childState == null ) { + if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") no ViewState"); continue; } - // Rely on StatusBarIcon for truth about visibility - if (!((StatusBarIconView) child).getStatusBarIcon().visible) { + if (!iconView.isIconVisible() || iconView.isIconBlocked()) { childState.hidden = true; + if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible"); continue; } @@ -175,8 +179,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { vs.initFrom(child); vs.alpha = 1.0f; - if (child instanceof StatusBarIconView) { - vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible; + if (child instanceof StatusIconDisplayable) { + vs.hidden = !((StatusIconDisplayable)child).isIconVisible(); } else { vs.hidden = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java index 58944c6ad571..945ed761c2b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java @@ -35,7 +35,7 @@ public interface DarkIconDispatcher { // Used to reapply darkness on an object, must have previously been added through // addDarkReceiver. - void applyDark(ImageView object); + void applyDark(DarkReceiver object); int DEFAULT_ICON_TINT = Color.WHITE; Rect sTmpRect = new Rect(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 4533aa5e2f76..4c100cdb99ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -54,6 +54,7 @@ import com.android.systemui.R; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.statusbar.policy.MobileSignalController.MobileState; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -848,6 +849,11 @@ public class NetworkControllerImpl extends BroadcastReceiver subs.add(addSignalController(i, i)); } mCallbackHandler.setSubs(subs); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + int key = mMobileSignalControllers.keyAt(i); + MobileSignalController controller = mMobileSignalControllers.get(key); + controller.notifyListeners(); + } } } String nosim = args.getString("nosim"); diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java index 1cbdfe81869d..7a9cdfd85645 100644 --- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java +++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java @@ -39,8 +39,7 @@ public class NotificationChannels extends SystemUI { public static String BATTERY = "BAT"; public static String HINTS = "HNT"; - @VisibleForTesting - static void createAll(Context context) { + public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); final NotificationChannel batteryChannel = new NotificationChannel(BATTERY, context.getString(R.string.notification_channel_battery), diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index acdd0c71b491..6f71e55b0d3c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -603,7 +603,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (row.defaultStream) { return activeRow.stream == STREAM_RING || activeRow.stream == STREAM_ALARM - || activeRow.stream == STREAM_VOICE_CALL; + || activeRow.stream == STREAM_VOICE_CALL + || activeRow.stream == STREAM_ACCESSIBILITY; } return false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index 4b455baee56b..149f2de06be4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -58,6 +58,7 @@ public class PowerUITest extends SysuiTestCase { public static final int BELOW_WARNING_BUCKET = -1; public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2); public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4); + private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis(); private HardwarePropertiesManager mHardProps; private WarningsUI mMockWarnings; private PowerUI mPowerUI; @@ -198,6 +199,7 @@ public class PowerUITest extends SysuiTestCase { when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); + mPowerUI.mBatteryLevel = 10; mPowerUI.start(); // unplugged device that would show the non-hybrid notification and the hybrid @@ -213,6 +215,7 @@ public class PowerUITest extends SysuiTestCase { when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); + mPowerUI.mBatteryLevel = 10; mPowerUI.start(); // unplugged device that would show the non-hybrid but not the hybrid @@ -254,13 +257,14 @@ public class PowerUITest extends SysuiTestCase { } @Test - public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnkown_returnsNoShow() { + public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnknown_returnsNoShow() { when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); mPowerUI.start(); - // Unknown battery status device that would show the neither due + // Unknown battery status device that would show the neither due to the battery status being + // unknown boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, @@ -295,6 +299,9 @@ public class PowerUITest extends SysuiTestCase { mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET); + + // reduce battery level to handle time based trigger -> level trigger interactions + mPowerUI.mBatteryLevel = 10; boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java index 23a34051bd7a..ab042d4ce491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -29,6 +31,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; +import android.app.NotificationChannel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.ArraySet; @@ -50,6 +53,7 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.List; import java.util.function.Consumer; @SmallTest @@ -274,4 +278,24 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mGroupRow.setBlockingHelperShowing(false); assertFalse(mGroupRow.isBlockingHelperShowing()); } + + @Test + public void testGetNumUniqueChildren_defaultChannel() { + assertEquals(1, mGroupRow.getNumUniqueChannels()); + } + + @Test + public void testGetNumUniqueChildren_multiChannel() { + List<ExpandableNotificationRow> childRows = + mGroupRow.getChildrenContainer().getNotificationChildren(); + // Give each child a unique channel id/name. + int i = 0; + for (ExpandableNotificationRow childRow : childRows) { + childRow.getEntry().channel = + new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT); + i++; + } + + assertEquals(3, mGroupRow.getNumUniqueChannels()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java index 64f34e009e7f..78cceeb3ff53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java @@ -87,7 +87,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception { - ExpandableNotificationRow row = spy(mHelper.createRow()); + ExpandableNotificationRow row = spy(createBlockableRowSpy()); row.setBlockingHelperShowing(true); when(row.isAttachedToWindow()).thenReturn(false); mBlockingHelperManager.setBlockingHelperRowForTest(row); @@ -100,7 +100,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception { - ExpandableNotificationRow row = spy(mHelper.createRow()); + ExpandableNotificationRow row = spy(createBlockableRowSpy()); row.setBlockingHelperShowing(true); when(row.isAttachedToWindow()).thenReturn(true); mBlockingHelperManager.setBlockingHelperRowForTest(row); @@ -113,7 +113,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_shown() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -125,7 +125,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception { - ExpandableNotificationRow groupRow = mHelper.createGroup(10); + ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10); groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -137,7 +137,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification() throws Exception { - ExpandableNotificationRow groupRow = mHelper.createGroup(1); + ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(1); // Explicitly get the children container & call getViewAtPosition on it instead of the row // as other factors such as view expansion may cause us to get the parent row back instead // of the child row. @@ -152,7 +152,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -162,7 +162,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -171,7 +171,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; // Hide the shade mBlockingHelperManager.setNotificationShadeExpanded(0f); @@ -180,9 +180,19 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { } @Test + public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception { + ExpandableNotificationRow row = createBlockableRowSpy(); + when(row.getIsNonblockable()).thenReturn(true); + row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + mBlockingHelperManager.setNotificationShadeExpanded(1f); + + assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow)); + } + + @Test public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup() throws Exception { - ExpandableNotificationRow groupRow = mHelper.createGroup(2); + ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(2); // Explicitly get the children container & call getViewAtPosition on it instead of the row // as other factors such as view expansion may cause us to get the parent row back instead // of the child row. @@ -195,7 +205,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testBlockingHelperShowAndDismiss() throws Exception{ - ExpandableNotificationRow row = spy(mHelper.createRow()); + ExpandableNotificationRow row = spy(createBlockableRowSpy()); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; when(row.isAttachedToWindow()).thenReturn(true); mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -211,4 +221,18 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { verify(mEntryManager).updateNotifications(); } + + private ExpandableNotificationRow createBlockableRowSpy() throws Exception { + ExpandableNotificationRow row = spy(mHelper.createRow()); + when(row.getIsNonblockable()).thenReturn(false); + return row; + } + + private ExpandableNotificationRow createBlockableGroupRowSpy(int numChildren) throws Exception { + ExpandableNotificationRow row = spy(mHelper.createGroup(numChildren)); + when(row.getIsNonblockable()).thenReturn(false); + return row; + } + + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java index 21f6750c0cdb..0ef2d051e4aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java @@ -268,9 +268,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Test public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception { NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = spy(mHelper.createRow()); row.setBlockingHelperShowing(true); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); mGutsManager.initializeNotificationInfo(row, notificationInfoView); @@ -285,7 +286,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), - any(), + eq(false), eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */); } @@ -293,9 +294,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Test public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception { NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = spy(mHelper.createRow()); row.setBlockingHelperShowing(false); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); mGutsManager.initializeNotificationInfo(row, notificationInfoView); @@ -310,7 +312,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), - any(), + eq(false), eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index c2cb5b9940eb..d86e947cdfcc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -76,7 +76,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -87,6 +86,7 @@ public class NotificationInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME; private static final int TEST_UID = 1; + private static final int MULTIPLE_CHANNEL_COUNT = 2; private static final String TEST_CHANNEL = "test_channel"; private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME"; @@ -157,7 +157,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -169,7 +169,7 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -177,7 +177,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); @@ -193,7 +193,7 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -204,7 +204,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -212,7 +212,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -224,7 +224,7 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockINotificationManager.getNumNotificationChannelsForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -232,8 +232,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -241,7 +240,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_BlockButton() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(VISIBLE, block.getVisibility()); @@ -252,7 +251,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_MinButton() throws Exception { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); @@ -267,7 +266,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, null); + }, null, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -278,7 +277,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -286,11 +285,11 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { - }, null, null); + }, null, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -299,11 +298,11 @@ public class NotificationInfoTest extends SysuiTestCase { public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, null); + }, null, true); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -315,7 +314,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, + null, true); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -325,7 +325,8 @@ public class NotificationInfoTest extends SysuiTestCase { @UiThreadTest public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, + null, true); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -333,8 +334,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_BlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, null, false, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false, + true); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText()); @@ -343,8 +344,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -354,7 +354,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @@ -363,7 +363,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -375,7 +375,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.minimize).performClick(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -387,7 +387,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { int originalImportance = mNotificationChannel.getImportance(); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( @@ -400,7 +400,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( @@ -420,7 +420,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */, - null /* nonBlockablePkgs */, + false /* isNonblockable */, true /* isForBlockingHelper */, false /* isUserSentimentNegative */); @@ -433,8 +433,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -445,7 +444,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -464,8 +463,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeMin() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -477,7 +475,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -496,7 +494,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testKeepUpdatesNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.handleCloseControls(true, false); @@ -512,7 +510,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -532,7 +530,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testMinUndoDoesNotMinNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -552,8 +550,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -566,8 +563,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -582,7 +578,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, null, null, Collections.singleton(TEST_PACKAGE_NAME)); + }, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -598,7 +594,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, null); + }, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -628,7 +624,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, null); + }, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -653,10 +649,10 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, null); + }, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -674,7 +670,8 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, + null, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -694,7 +691,7 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -710,8 +707,7 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -723,8 +719,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_block() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -736,8 +731,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNoHeaderOnConfirmation() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -748,8 +742,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHeaderOnUndo() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java index 2792d8c45865..07ac11b82e8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java @@ -1,17 +1,23 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.phone.StatusBarIconHolder; import com.android.systemui.statusbar.phone.StatusBarIconList; +import com.android.systemui.statusbar.phone.StatusBarIconList.Slot; +import java.util.ArrayList; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,45 +47,125 @@ public class StatusBarIconListTest extends SysuiTestCase { @Test public void testAddSlotSlidesIcons() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIcon = mock(StatusBarIcon.class); - statusBarIconList.setIcon(0, sbIcon); + StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(0, sbHolder); statusBarIconList.getSlotIndex("zzz"); // new content added in front - assertNull(statusBarIconList.getIcon(0)); - assertEquals(sbIcon, statusBarIconList.getIcon(1)); + assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); + assertEquals(sbHolder, statusBarIconList.getIcon(1, TAG_PRIMARY)); } @Test public void testGetAndSetIcon() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIconA = mock(StatusBarIcon.class); - StatusBarIcon sbIconB = mock(StatusBarIcon.class); - statusBarIconList.setIcon(0, sbIconA); - statusBarIconList.setIcon(1, sbIconB); - assertEquals(sbIconA, statusBarIconList.getIcon(0)); - assertEquals(sbIconB, statusBarIconList.getIcon(1)); - assertNull(statusBarIconList.getIcon(2)); // icon not set + StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(0, sbHolderA); + statusBarIconList.setIcon(1, sbHolderB); + assertEquals(sbHolderA, statusBarIconList.getIcon(0, TAG_PRIMARY)); + assertEquals(sbHolderB, statusBarIconList.getIcon(1, TAG_PRIMARY)); + assertNull(statusBarIconList.getIcon(2, TAG_PRIMARY)); // icon not set } @Test public void testRemoveIcon() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIconA = mock(StatusBarIcon.class); - StatusBarIcon sbIconB = mock(StatusBarIcon.class); - statusBarIconList.setIcon(0, sbIconA); - statusBarIconList.setIcon(1, sbIconB); - statusBarIconList.removeIcon(0); - assertNull(statusBarIconList.getIcon(0)); // icon not set + StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(0, sbHolderA); + statusBarIconList.setIcon(1, sbHolderB); + statusBarIconList.removeIcon(0, TAG_PRIMARY); + assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); // icon not set } @Test - public void testGetViewIndex() { + public void testGetViewIndex_NoMultiples() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIcon = mock(StatusBarIcon.class); - statusBarIconList.setIcon(2, sbIcon); - assertEquals(0, statusBarIconList.getViewIndex(2)); // Icon for item 2 is 0th child view. - statusBarIconList.setIcon(0, sbIcon); - assertEquals(0, statusBarIconList.getViewIndex(0)); // Icon for item 0 is 0th child view, - assertEquals(1, statusBarIconList.getViewIndex(2)); // and item 2 is now 1st child view. + StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(2, sbHolder); + // Icon for item 2 is 0th child view. + assertEquals(0, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + statusBarIconList.setIcon(0, sbHolder); + // Icon for item 0 is 0th child view, + assertEquals(0, statusBarIconList.getViewIndex(0, TAG_PRIMARY)); + // and item 2 is now 1st child view. + assertEquals(1, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); } + @Test + public void testGetViewIndex_MultipleIconsPerSlot() { + StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); + StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); + + statusBarIconList.setIcon(2, sbHolder); // item 2, one icon 0th child + + // All of these can be added to the same slot + // no tag bc it defaults to 0 + StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class); + int sb3Tag = 1; + when(sbHolder3.getTag()).thenReturn(sb3Tag); + StatusBarIconHolder sbHolder4 = mock(StatusBarIconHolder.class); + int sb4Tag = 2; + when(sbHolder4.getTag()).thenReturn(sb4Tag); + + // Put a holder at slot 1, verify that it is first + statusBarIconList.setIcon(1, sbHolder2); + assertEquals(0, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); + + // Put another holder at slot 1, verify it's index 0 and the rest come after + statusBarIconList.setIcon(1, sbHolder3); + assertEquals(0, statusBarIconList.getViewIndex(1, sb3Tag)); + assertEquals(1, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); + // First icon should be at the end + assertEquals(2, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + + // Put another one in there just for good measure + statusBarIconList.setIcon(1, sbHolder4); + assertEquals(0, statusBarIconList.getViewIndex(1, sb4Tag)); + assertEquals(1, statusBarIconList.getViewIndex(1, sb3Tag)); + assertEquals(2, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); + assertEquals(3, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + } + + /** + * StatusBarIconList.Slot tests + */ + + @Test + public void testSlot_OrderIsPreserved() { + Slot testSlot = new Slot("test_name", null); + + // no tag bc it defaults to 0 + StatusBarIconHolder sbHolder1 = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class); + int sb2Tag = 1; + when(sbHolder2.getTag()).thenReturn(sb2Tag); + StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class); + int sb3Tag = 2; + when(sbHolder3.getTag()).thenReturn(sb3Tag); + + ArrayList<StatusBarIconHolder> expected = new ArrayList<>(); + expected.add(sbHolder1); + expected.add(sbHolder2); + expected.add(sbHolder3); + + + // Add 3 icons in the same slot, and verify that the list we get is equal to what we gave + for (StatusBarIconHolder holder : expected) { + testSlot.addHolder(holder); + } + assertTrue(listsEqual(expected, testSlot.getHolderList())); + } + + private boolean listsEqual(List<StatusBarIconHolder> list1, List<StatusBarIconHolder> list2) { + if (list1.size() != list2.size()) return false; + + for (int i = 0; i < list1.size(); i++) { + if (!list1.get(i).equals(list2.get(i))) { + return false; + } + } + + return true; + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 5db77925b25e..72b0156d25f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -14,10 +14,17 @@ package com.android.systemui.statusbar.phone; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.graphics.Rect; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -25,9 +32,14 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarMobileView; +import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.utils.leaks.LeakCheckedTest; @@ -50,50 +62,119 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { public void testSetCalledOnAdd_IconManager() { LinearLayout layout = new LinearLayout(mContext); TestIconManager manager = new TestIconManager(layout); - StatusBarIcon icon = mock(StatusBarIcon.class); - - manager.onIconAdded(0, "test_slot", false, icon); - verify(manager.mMock).set(eq(icon)); + testCallOnAdd_forManager(manager); } @Test public void testSetCalledOnAdd_DarkIconManager() { LinearLayout layout = new LinearLayout(mContext); TestDarkIconManager manager = new TestDarkIconManager(layout); - StatusBarIcon icon = mock(StatusBarIcon.class); + testCallOnAdd_forManager(manager); + } + + + private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) { + StatusBarIconHolder holder = holderForType(TYPE_ICON); + manager.onIconAdded(0, "test_slot", false, holder); + assertTrue("Expected StatusBarIconView", + (manager.getViewAt(0) instanceof StatusBarIconView)); - manager.onIconAdded(0, "test_slot", false, icon); - verify(manager.mMock).set(eq(icon)); + holder = holderForType(TYPE_WIFI); + manager.onIconAdded(1, "test_wifi", false, holder); + assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView); + + holder = holderForType(TYPE_MOBILE); + manager.onIconAdded(2, "test_mobile", false, holder); + assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView); } - private static class TestDarkIconManager extends DarkIconManager { + private StatusBarIconHolder holderForType(int type) { + switch (type) { + case TYPE_MOBILE: + return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class)); + + case TYPE_WIFI: + return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class)); - private final StatusBarIconView mMock; + case TYPE_ICON: + default: + return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class)); + } + } + + private static class TestDarkIconManager extends DarkIconManager + implements TestableIconManager { public TestDarkIconManager(LinearLayout group) { super(group); - mMock = mock(StatusBarIconView.class); } @Override - protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { - return mMock; + public StatusIconDisplayable getViewAt(int index) { + return (StatusIconDisplayable) mGroup.getChildAt(index); } - } - private static class TestIconManager extends IconManager { + @Override + protected StatusBarIconView addIcon(int index, String slot, boolean blocked, + StatusBarIcon icon) { + StatusBarIconView mock = mock(StatusBarIconView.class); + mGroup.addView(mock, index); + + return mock; + } - private final StatusBarIconView mMock; + @Override + protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { + StatusBarWifiView mock = mock(StatusBarWifiView.class); + mGroup.addView(mock, index); + return mock; + } + @Override + protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { + StatusBarMobileView mock = mock(StatusBarMobileView.class); + mGroup.addView(mock, index); + + return mock; + } + } + + private static class TestIconManager extends IconManager implements TestableIconManager { public TestIconManager(ViewGroup group) { super(group); - mMock = mock(StatusBarIconView.class); } @Override - protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { - return mMock; + public StatusIconDisplayable getViewAt(int index) { + return (StatusIconDisplayable) mGroup.getChildAt(index); + } + + @Override + protected StatusBarIconView addIcon(int index, String slot, boolean blocked, + StatusBarIcon icon) { + StatusBarIconView mock = mock(StatusBarIconView.class); + mGroup.addView(mock, index); + + return mock; + } + + @Override + protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { + StatusBarWifiView mock = mock(StatusBarWifiView.class); + mGroup.addView(mock, index); + return mock; + } + + @Override + protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { + StatusBarMobileView mock = mock(StatusBarMobileView.class); + mGroup.addView(mock, index); + + return mock; } } + private interface TestableIconManager { + StatusIconDisplayable getViewAt(int index); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index 6b501af95097..8e34685cceec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -19,6 +19,9 @@ import android.testing.LeakCheck; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; +import java.util.List; public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> implements StatusBarIconController { @@ -53,12 +56,23 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override - public void setIconVisibility(String slotTty, boolean b) { + public void setSignalIcon(String slot, WifiIconState state) { + } + + @Override + public void setMobileIcons(String slot, List<MobileIconState> states) { + } + @Override + public void setIconVisibility(String slotTty, boolean b) { } @Override - public void removeIcon(String slot) { + public void removeIcon(String slot, int tag) { + } + @Override + public void removeAllIconsForSlot(String slot) { } + } diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 160525294c0f..934ad882a635 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -413,6 +413,15 @@ message WifiLog { // Indicates the number of times we got an interface down in softap mode. optional int32 num_soft_ap_interface_down = 103; + + // Indicates the number of scan requests from external apps. + optional int32 num_external_app_oneshot_scan_requests = 104; + + // Indicates the number of times a scan request from an external foreground app was throttled. + optional int32 num_external_foreground_app_oneshot_scan_requests_throttled = 105; + + // Indicates the number of times a scan request from an external background app was throttled. + optional int32 num_external_background_app_oneshot_scan_requests_throttled = 106; } // Information that gets logged for every WiFi connection. diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index f6ff359d8c19..40f947607b1e 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -266,9 +266,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("LOCK") private boolean mValue; - public DebugFlag(String key) { + public DebugFlag(String key, boolean defaultValue) { mKey = key; - refresh(); + mValue = SystemProperties.getBoolean(key, defaultValue); } void refresh() { @@ -290,7 +290,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ private static final class DebugFlags { static final DebugFlag FLAG_OPTIMIZE_START_INPUT = - new DebugFlag("debug.optimize_startinput"); + new DebugFlag("debug.optimize_startinput", false); } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index d09a161d1ef4..bde6bd8db6fd 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -931,7 +931,7 @@ public class IpSecService extends IIpSecService.Stub { return mPort; } - public FileDescriptor getSocket() { + public FileDescriptor getFileDescriptor() { return mSocket; } @@ -1065,7 +1065,10 @@ public class IpSecService extends IIpSecService.Stub { public synchronized IpSecSpiResponse allocateSecurityParameterIndex( String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException { checkInetAddress(destinationAddress); - /* requestedSpi can be anything in the int range, so no check is needed. */ + // RFC 4303 Section 2.1 - 0=local, 1-255=reserved. + if (requestedSpi > 0 && requestedSpi < 256) { + throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255."); + } checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex"); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 08e5d44c8832..f756afb75377 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4493,8 +4493,8 @@ public class ActivityManagerService extends IActivityManager.Stub StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED, component.app.uid, component.realActivity.getPackageName(), component.realActivity.getShortClassName(), resumed ? - StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND : - StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND); + StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND : + StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND); if (resumed) { if (mUsageStatsService != null) { mUsageStatsService.reportEvent(component.realActivity, component.userId, @@ -13179,10 +13179,6 @@ public class ActivityManagerService extends IActivityManager.Stub + android.Manifest.permission.SHUTDOWN); } - // TODO: Where should the corresponding '1' (start) write go? - StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, - StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF); - boolean timedout = false; synchronized(this) { @@ -14830,7 +14826,7 @@ public class ActivityManagerService extends IActivityManager.Stub .setPackage("android") .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); broadcastIntent(null, intent, null, null, 0, null, null, null, - OP_NONE, null, true, false, UserHandle.USER_ALL); + OP_NONE, null, false, false, UserHandle.USER_ALL); } finally { Binder.restoreCallingIdentity(ident); } @@ -15197,6 +15193,12 @@ public class ActivityManagerService extends IActivityManager.Stub crashInfo.throwFileName, crashInfo.throwLineNumber); + StatsLog.write(StatsLog.APP_CRASH_OCCURRED, + Binder.getCallingUid(), + eventType, + processName, + Binder.getCallingPid()); + addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo); mAppErrors.crashApplication(r, crashInfo); @@ -15367,6 +15369,9 @@ public class ActivityManagerService extends IActivityManager.Stub EventLog.writeEvent(EventLogTags.AM_WTF, UserHandle.getUserId(callingUid), callingPid, processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage); + StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName, + callingPid); + addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo); return r; @@ -15488,19 +15493,6 @@ public class ActivityManagerService extends IActivityManager.Stub final String dropboxTag = processClass(process) + "_" + eventType; if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; - // Log to StatsLog before the rate-limiting. - // The logging below is adapated from appendDropboxProcessHeaders. - StatsLog.write(StatsLog.DROPBOX_ERROR_CHANGED, - process != null ? process.uid : -1, - dropboxTag, - processName, - process != null ? process.pid : -1, - (process != null && process.info != null) ? - (process.info.isInstantApp() ? 1 : 0) : -1, - activity != null ? activity.shortComponentName : null, - activity != null ? activity.packageName : null, - process != null ? (process.isInterestingToUserLocked() ? 1 : 0) : -1); - // Rate-limit how often we're willing to do the heavy lifting below to // collect and record logs; currently 5 logs per 10 second period. final long now = SystemClock.elapsedRealtime(); @@ -19780,6 +19772,7 @@ public class ActivityManagerService extends IActivityManager.Stub catPw.flush(); } dropBuilder.append(catSw.toString()); + StatsLog.write(StatsLog.LOW_MEM_REPORTED); addErrorToDropBox("lowmem", null, "system_server", null, null, tag.toString(), dropBuilder.toString(), null, null); //Slog.i(TAG, "Sent to dropbox:"); diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 724dd3fd9847..47d0423550c4 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -489,7 +489,7 @@ class ActivityMetricsLogger { builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name); mMetricsLogger.write(builder); StatsLog.write( - StatsLog.APP_START_CANCEL_CHANGED, + StatsLog.APP_START_CANCELED, info.launchedActivity.appInfo.uid, info.launchedActivity.packageName, convertAppStartTransitionType(type), @@ -561,7 +561,7 @@ class ActivityMetricsLogger { packageOptimizationInfo.getCompilationFilter()); mMetricsLogger.write(builder); StatsLog.write( - StatsLog.APP_START_CHANGED, + StatsLog.APP_START_OCCURRED, info.applicationInfo.uid, info.packageName, convertAppStartTransitionType(info.type), @@ -582,15 +582,15 @@ class ActivityMetricsLogger { private int convertAppStartTransitionType(int tronType) { if (tronType == TYPE_TRANSITION_COLD_LAUNCH) { - return StatsLog.APP_START_CHANGED__TYPE__COLD; + return StatsLog.APP_START_OCCURRED__TYPE__COLD; } if (tronType == TYPE_TRANSITION_WARM_LAUNCH) { - return StatsLog.APP_START_CHANGED__TYPE__WARM; + return StatsLog.APP_START_OCCURRED__TYPE__WARM; } if (tronType == TYPE_TRANSITION_HOT_LAUNCH) { - return StatsLog.APP_START_CHANGED__TYPE__HOT; + return StatsLog.APP_START_OCCURRED__TYPE__HOT; } - return StatsLog.APP_START_CHANGED__TYPE__APP_START_TRANSITION_TYPE_UNKNOWN; + return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN; } void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) { @@ -611,12 +611,12 @@ class ActivityMetricsLogger { info.currentTransitionProcessRunning ? 1 : 0); mMetricsLogger.write(builder); StatsLog.write( - StatsLog.APP_START_FULLY_DRAWN_CHANGED, + StatsLog.APP_START_FULLY_DRAWN, info.launchedActivity.appInfo.uid, info.launchedActivity.packageName, restoredFromBundle - ? StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITH_BUNDLE - : StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITHOUT_BUNDLE, + ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE + : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE, info.launchedActivity.info.name, info.currentTransitionProcessRunning, startupTimeMs); diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index b2872e4d7f5a..7ee20fa2501c 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -47,6 +47,7 @@ import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.StatsLog; import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -1039,6 +1040,8 @@ class AppErrors { Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } + StatsLog.write(StatsLog.ANR_OCCURRED, app.uid, app.processName, + activity == null ? "unknown": activity.shortComponentName, annotation); mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index b338029d4e86..8ecd93e65b08 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -337,7 +337,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mStats) { mStats.noteProcessStartLocked(name, uid); StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, - StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED); + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__STARTED); } } @@ -345,15 +345,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mStats) { mStats.noteProcessCrashLocked(name, uid); StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, - StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED); + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__CRASHED); } } void noteProcessAnr(String name, int uid) { synchronized (mStats) { mStats.noteProcessAnrLocked(name, uid); - StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, - StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED); } } @@ -361,7 +359,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mStats) { mStats.noteProcessFinishLocked(name, uid); StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, - StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED); + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__FINISHED); } } @@ -768,8 +766,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub enforceCallingPermission(); synchronized (mStats) { mStats.noteVideoOnLocked(uid); - StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uid, null, - StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON); + StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid, null, + StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__ON); } } @@ -777,8 +775,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub enforceCallingPermission(); synchronized (mStats) { mStats.noteVideoOffLocked(uid); - StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, uid, - null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF); + StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid, + null, StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__OFF); } } @@ -795,8 +793,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub enforceCallingPermission(); synchronized (mStats) { mStats.noteResetVideoLocked(); - StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, -1, null, - StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__RESET); + StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, -1, null, + StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__RESET); } } diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 262a2f8ea817..92d3772e0607 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -1470,6 +1470,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death proto.end(userToken); } proto.flush(); + mPerformanceMap.clear(); + mCryptoPerformanceMap.clear(); } @Override diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 1dbcfd6a5bdf..40d2a3a4a094 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -19,7 +19,6 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.AlarmManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -101,18 +100,19 @@ public final class IdleController extends StateController { final class IdlenessTracker extends BroadcastReceiver { private AlarmManager mAlarm; - private PendingIntent mIdleTriggerIntent; - boolean mIdle; - boolean mScreenOn; + + // After construction, mutations of idle/screen-on state will only happen + // on the main looper thread, either in onReceive() or in an alarm callback. + private boolean mIdle; + private boolean mScreenOn; + + private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> { + handleIdleTrigger(); + }; public IdlenessTracker() { mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE) - .setPackage("android") - .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); - // At boot we presume that the user has just "interacted" with the // device in some meaningful way. mIdle = false; @@ -150,7 +150,7 @@ public final class IdleController extends StateController { } mScreenOn = true; //cancel the alarm - mAlarm.cancel(mIdleTriggerIntent); + mAlarm.cancel(mIdleAlarmListener); if (mIdle) { // possible transition to not-idle mIdle = false; @@ -169,20 +169,24 @@ public final class IdleController extends StateController { } mScreenOn = false; mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, - when, mIdleWindowSlop, mIdleTriggerIntent); + when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null); } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) { - // idle time starts now. Do not set mIdle if screen is on. - if (!mIdle && !mScreenOn) { - if (DEBUG) { - Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis()); - } - mIdle = true; - reportNewIdleState(mIdle); - } else { - if (DEBUG) { - Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle=" - + mIdle + " screen=" + mScreenOn); - } + handleIdleTrigger(); + } + } + + private void handleIdleTrigger() { + // idle time starts now. Do not set mIdle if screen is on. + if (!mIdle && !mScreenOn) { + if (DEBUG) { + Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis()); + } + mIdle = true; + reportNewIdleState(mIdle); + } else { + if (DEBUG) { + Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle=" + + mIdle + " screen=" + mScreenOn); } } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index c445f73e5042..5955c9c3dd0a 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -2513,6 +2513,8 @@ public class GnssLocationProvider implements LocationProviderInterface { * this handler. */ private void handleInitialize() { + native_init_once(); + /* * A cycle of native_init() and native_cleanup() is needed so that callbacks are * registered after bootup even when location is disabled. @@ -2899,6 +2901,8 @@ public class GnssLocationProvider implements LocationProviderInterface { private static native boolean native_is_gnss_configuration_supported(); + private static native void native_init_once(); + private native boolean native_init(); private native void native_cleanup(); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index f617964481bf..4b58d537e844 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2079,11 +2079,6 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { - return mRecoverableKeyStoreManager.generateAndStoreKey(alias); - } - - @Override public @Nullable String generateKey(@NonNull String alias) throws RemoteException { return mRecoverableKeyStoreManager.generateKey(alias); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 1dab5920288b..ff4c6782accb 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -672,40 +672,6 @@ public class RecoverableKeyStoreManager { } /** - * Deprecated - * Generates a key named {@code alias} in the recoverable store for the calling uid. Then - * returns the raw key material. - * - * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes. - * - * @deprecated - * @hide - */ - public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { - checkRecoverKeyStorePermission(); - int uid = Binder.getCallingUid(); - int userId = UserHandle.getCallingUserId(); - - PlatformEncryptionKey encryptionKey; - try { - encryptionKey = mPlatformKeyManager.getEncryptKey(userId); - } catch (NoSuchAlgorithmException e) { - // Impossible: all algorithms must be supported by AOSP - throw new RuntimeException(e); - } catch (KeyStoreException | UnrecoverableKeyException e) { - throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } catch (InsecureUserException e) { - throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); - } - - try { - return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); - } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { - throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } - } - - /** * Destroys the session with the given {@code sessionId}. */ public void closeSession(@NonNull String sessionId) throws RemoteException { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index b7842d5365b5..d5a32aa30936 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -982,7 +982,11 @@ abstract public class ManagedServices { Slog.w(TAG, getCaption() + " binding died: " + name); synchronized (mMutex) { mServicesBinding.remove(servicesBindingTag); - mContext.unbindService(this); + try { + mContext.unbindService(this); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "failed to unbind " + name, e); + } if (!mServicesRebinding.contains(servicesBindingTag)) { mServicesRebinding.add(servicesBindingTag); mHandler.postDelayed(new Runnable() { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 31b0461b53b4..f31ca0a2fa09 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -182,6 +182,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.ArrayUtils; @@ -898,6 +899,8 @@ public class NotificationManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { + // update system notification channels + SystemNotificationChannels.createAll(context); mZenModeHelper.updateDefaultZenRules(); mRankingHelper.onLocaleChanged(context, ActivityManager.getCurrentUser()); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 24abf8657b88..9d3f48b428ce 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -484,11 +484,11 @@ public class Installer extends SystemService { } } - public void installApkVerity(String filePath, FileDescriptor verityInput) + public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize) throws InstallerException { if (!checkBeforeRemote()) return; try { - mInstalld.installApkVerity(filePath, verityInput); + mInstalld.installApkVerity(filePath, verityInput, contentSize); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 98073429faab..892fa12ddf52 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -450,11 +450,9 @@ public class PackageDexOptimizer { for (String isa : dexCodeInstructionSets) { try { - String[] status = DexFile.getDexFileOptimizationStatus(path, isa); - String compilationStatus = status[0]; - String compilationReason = status[1]; - pw.println(isa + ": [status=" + compilationStatus - +"] reason=[" + compilationReason + "]"); + DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa); + pw.println(isa + ": [status=" + info.getStatus() + +"] [reason=" + info.getReason() + "]"); } catch (IOException ioe) { pw.println(isa + ": [Exception]: " + ioe.getMessage()); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7b77c9660dae..cb1972f17515 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17322,8 +17322,11 @@ public class PackageManagerService extends IPackageManager.Stub if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { - mInstaller.installApkVerity(apkPath, fd); - } catch (InstallerException e) { + final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath); + mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); + mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); + } catch (InstallerException | IOException | DigestException | + NoSuchAlgorithmException e) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); return; diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index cdafe5715ffc..9c2ad4635d58 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -527,9 +527,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { String compilationFilter; try { String isa = VMRuntime.getInstructionSet(abi); - String[] stats = DexFile.getDexFileOptimizationStatus(info.getBaseCodePath(), isa); - compilationFilter = stats[0]; - compilationReason = stats[1]; + DexFile.OptimizationInfo optInfo = + DexFile.getDexFileOptimizationInfo(info.getBaseCodePath(), isa); + compilationFilter = optInfo.getStatus(); + compilationReason = optInfo.getReason(); } catch (FileNotFoundException e) { Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e); compilationFilter = "error"; 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..e56caf849aac 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; + + try { + mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId); + } catch (RemoteException e) { + Slog.e(TAG, "Error informing keystore of screen lock", e); + } } @Override // Binder interface @@ -130,4 +143,4 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { pw.println(prefix + "mTrusted=" + mTrusted); pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index d2d0e60dc742..180f34355c94 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -26,6 +26,7 @@ import android.system.Os; import android.util.apk.ApkSignatureVerifier; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; +import android.util.Pair; import android.util.Slog; import java.io.FileDescriptor; @@ -59,12 +60,15 @@ abstract public class VerityUtils { return SetupResult.skipped(); } - shm = generateApkVerityIntoSharedMemory(apkPath, signedRootHash); + Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath, + signedRootHash); + shm = result.first; + int contentSize = result.second; FileDescriptor rfd = shm.getFileDescriptor(); if (rfd == null || !rfd.valid()) { return SetupResult.failed(); } - return SetupResult.ok(Os.dup(rfd)); + return SetupResult.ok(Os.dup(rfd), contentSize); } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException | SignatureNotFoundException | ErrnoException e) { Slog.e(TAG, "Failed to set up apk verity: ", e); @@ -85,10 +89,20 @@ abstract public class VerityUtils { } /** - * Returns a {@code SharedMemory} that contains Merkle tree and fsverity headers for the given - * apk, in the form that can immediately be used for fsverity setup. + * {@see ApkSignatureVerifier#getVerityRootHash(String)}. */ - private static SharedMemory generateApkVerityIntoSharedMemory( + public static byte[] getVerityRootHash(@NonNull String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + return ApkSignatureVerifier.getVerityRootHash(apkPath); + } + + /** + * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains + * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used + * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has + * length equals to the returned {@code Integer}. + */ + private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory( String apkPath, byte[] expectedRootHash) throws IOException, SecurityException, DigestException, NoSuchAlgorithmException, SignatureNotFoundException { @@ -101,6 +115,7 @@ abstract public class VerityUtils { throw new SecurityException("Locally generated verity root hash does not match"); } + int contentSize = shmBufferFactory.getBufferLimit(); SharedMemory shm = shmBufferFactory.releaseSharedMemory(); if (shm == null) { throw new IllegalStateException("Failed to generate verity tree into shared memory"); @@ -108,7 +123,7 @@ abstract public class VerityUtils { if (!shm.setProtect(PROT_READ)) { throw new SecurityException("Failed to set up shared memory correctly"); } - return shm; + return Pair.create(shm, contentSize); } public static class SetupResult { @@ -123,22 +138,24 @@ abstract public class VerityUtils { private final int mCode; private final FileDescriptor mFileDescriptor; + private final int mContentSize; - public static SetupResult ok(@NonNull FileDescriptor fileDescriptor) { - return new SetupResult(RESULT_OK, fileDescriptor); + public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) { + return new SetupResult(RESULT_OK, fileDescriptor, contentSize); } public static SetupResult skipped() { - return new SetupResult(RESULT_SKIPPED, null); + return new SetupResult(RESULT_SKIPPED, null, -1); } public static SetupResult failed() { - return new SetupResult(RESULT_FAILED, null); + return new SetupResult(RESULT_FAILED, null, -1); } - private SetupResult(int code, FileDescriptor fileDescriptor) { + private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) { this.mCode = code; this.mFileDescriptor = fileDescriptor; + this.mContentSize = contentSize; } public boolean isFailed() { @@ -152,6 +169,10 @@ abstract public class VerityUtils { public @NonNull FileDescriptor getUnownedFileDescriptor() { return mFileDescriptor; } + + public int getContentSize() { + return mContentSize; + } } /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */ @@ -188,5 +209,9 @@ abstract public class VerityUtils { mShm = null; return tmp; } + + public int getBufferLimit() { + return mBuffer == null ? -1 : mBuffer.limit(); + } } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index d252a56ddf6b..753394d25b81 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -39,6 +39,7 @@ import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Bundle; import android.os.Environment; +import android.os.FileUtils; import android.os.IBinder; import android.os.IStatsCompanionService; import android.os.IStatsManager; @@ -68,14 +69,21 @@ import com.android.internal.os.KernelUidCpuFreqTimeReader; import com.android.internal.os.KernelWakelockReader; import com.android.internal.os.KernelWakelockStats; import com.android.internal.os.PowerProfile; +import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** @@ -89,14 +97,17 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { * How long to wait on an individual subsystem to return its stats. */ private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; + private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1); public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity"; + public static final String CONFIG_DIR = "/data/misc/stats-service"; static final String TAG = "StatsCompanionService"; static final boolean DEBUG = false; public static final int CODE_DATA_BROADCAST = 1; public static final int CODE_SUBSCRIBER_BROADCAST = 1; + public static final int DEATH_THRESHOLD = 10; private final Context mContext; private final AlarmManager mAlarmManager; @@ -119,6 +130,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { new StatFs(Environment.getRootDirectory().getAbsolutePath()); private final StatFs mStatFsTemp = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath()); + @GuardedBy("sStatsdLock") + private final HashSet<Long> mDeathTimeMillis = new HashSet<>(); + @GuardedBy("sStatsdLock") + private final HashMap<Long, String> mDeletedFiles = new HashMap<>(); private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; @@ -156,7 +171,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { informAllUidsLocked(context); } catch (RemoteException e) { Slog.e(TAG, "Failed to inform statsd latest update of all apps", e); - forgetEverything(); + forgetEverythingLocked(); } } } @@ -751,7 +766,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { }); } - private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) { + private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) { long token = Binder.clearCallingIdentity(); if (mWifiManager == null) { mWifiManager = @@ -919,8 +934,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullKernelUidCpuActiveTime(tagId, ret); break; } - case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: { - pullWifiActivityEnergyInfo(tagId, ret); + case StatsLog.WIFI_ACTIVITY_INFO: { + pullWifiActivityInfo(tagId, ret); break; } case StatsLog.MODEM_ACTIVITY_INFO: { @@ -1055,7 +1070,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0); } catch (RemoteException e) { Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e); - forgetEverything(); + forgetEverythingLocked(); } // Setup broadcast receiver for updates. IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED); @@ -1087,7 +1102,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { Slog.i(TAG, "Told statsd that StatsCompanionService is alive."); } catch (RemoteException e) { Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e); - forgetEverything(); + forgetEverythingLocked(); } } } @@ -1096,18 +1111,60 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override public void binderDied() { Slog.i(TAG, "Statsd is dead - erase all my knowledge."); - forgetEverything(); + synchronized (sStatsdLock) { + long now = SystemClock.elapsedRealtime(); + for (Long timeMillis : mDeathTimeMillis) { + long ageMillis = now - timeMillis; + if (ageMillis > MILLIS_IN_A_DAY) { + mDeathTimeMillis.remove(timeMillis); + } + } + for (Long timeMillis : mDeletedFiles.keySet()) { + long ageMillis = now - timeMillis; + if (ageMillis > MILLIS_IN_A_DAY * 7) { + mDeletedFiles.remove(timeMillis); + } + } + mDeathTimeMillis.add(now); + if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) { + mDeathTimeMillis.clear(); + File[] configs = FileUtils.listFilesOrEmpty(new File(CONFIG_DIR)); + if (configs.length > 0) { + String fileName = configs[0].getName(); + if (configs[0].delete()) { + mDeletedFiles.put(now, fileName); + } + } + } + forgetEverythingLocked(); + } } } - private void forgetEverything() { + private void forgetEverythingLocked() { + sStatsd = null; + mContext.unregisterReceiver(mAppUpdateReceiver); + mContext.unregisterReceiver(mUserUpdateReceiver); + mContext.unregisterReceiver(mShutdownEventReceiver); + cancelAnomalyAlarm(); + cancelPullingAlarm(); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; + synchronized (sStatsdLock) { - sStatsd = null; - mContext.unregisterReceiver(mAppUpdateReceiver); - mContext.unregisterReceiver(mUserUpdateReceiver); - mContext.unregisterReceiver(mShutdownEventReceiver); - cancelAnomalyAlarm(); - cancelPullingAlarm(); + writer.println("Number of configuration files deleted: " + mDeletedFiles.size()); + if (mDeletedFiles.size() > 0) { + writer.println(" timestamp, deleted file name"); + } + long lastBootMillis = + SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime(); + for (Long elapsedMillis : mDeletedFiles.keySet()) { + long deletionMillis = lastBootMillis + elapsedMillis; + writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis)); + } } } diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp index ecf1a33539e9..f7ca363e54a1 100644 --- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp +++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp @@ -231,6 +231,13 @@ static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jin return nullptr; } bandConfigHal = module.bands[0]; + + /* Prefer FM to workaround possible program list fetching limitation + * (if tuner scans only configured band for programs). */ + auto fmIt = std::find_if(module.bands.begin(), module.bands.end(), + [](const BandConfig & band) { return utils::isFm(band.type); }); + if (fmIt != module.bands.end()) bandConfigHal = *fmIt; + if (bandConfigHal.spacings.size() > 1) { bandConfigHal.spacings = hidl_vec<uint32_t>({ *std::min_element( bandConfigHal.spacings.begin(), bandConfigHal.spacings.end()) }); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index dcee15106f8a..21fea1c910cb 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -1125,6 +1125,16 @@ Return<void> GnssBatchingCallback::gnssLocationBatchCb( } static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { + gnssHal_V1_1 = IGnss_V1_1::getService(); + if (gnssHal_V1_1 == nullptr) { + ALOGD("gnssHal 1.1 was null, trying 1.0"); + gnssHal = IGnss_V1_0::getService(); + } else { + gnssHal = gnssHal_V1_1; + } +} + +static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) { method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V"); method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); @@ -1175,15 +1185,6 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus); } - // TODO(b/31632518) - gnssHal_V1_1 = IGnss_V1_1::getService(); - if (gnssHal_V1_1 == nullptr) { - ALOGD("gnssHal 1.1 was null, trying 1.0"); - gnssHal = IGnss_V1_0::getService(); - } else { - gnssHal = gnssHal_V1_1; - } - if (gnssHal != nullptr) { gnssHalDeathRecipient = new GnssDeathRecipient(); hardware::Return<bool> linked = gnssHal->linkToDeath( @@ -2068,6 +2069,8 @@ static const JNINativeMethod sMethods[] = { {"native_is_gnss_configuration_supported", "()Z", reinterpret_cast<void *>( android_location_gpsLocationProvider_is_gnss_configuration_supported)}, + {"native_init_once", "()V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_init_once)}, {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)}, {"native_cleanup", "()V", reinterpret_cast<void *>( android_location_GnssLocationProvider_cleanup)}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 71c2ea18eb1b..1c9782fa5565 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -78,23 +78,6 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { return false; } - @Override - public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist, - boolean parent) { - return false; - } - - @Override - public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId, - boolean parent) { - return null; - } - - @Override - public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) { - return false; - } - public boolean isUsingUnifiedPassword(ComponentName who) { return true; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ffa7311f607d..90e8a9cd74e1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -820,7 +820,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String ATTR_VALUE = "value"; - private static final String TAG_PASSWORD_BLACKLIST = "password-blacklist"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = @@ -961,9 +960,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Default title of confirm credentials screen String organizationName = null; - // The blacklist data is stored in a file whose name is stored in the XML - String passwordBlacklistFile = null; - // The component name of the backup transport which has to be used if backups are mandatory // or null if backups are not mandatory. ComponentName mandatoryBackupTransport = null; @@ -1053,11 +1049,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.endTag(null, TAG_MIN_PASSWORD_NONLETTER); } } - if (passwordBlacklistFile != null) { - out.startTag(null, TAG_PASSWORD_BLACKLIST); - out.attribute(null, ATTR_VALUE, passwordBlacklistFile); - out.endTag(null, TAG_PASSWORD_BLACKLIST); - } if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) { out.startTag(null, TAG_MAX_TIME_TO_UNLOCK); out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock)); @@ -1313,8 +1304,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { minimumPasswordMetrics.nonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); - } else if (TAG_PASSWORD_BLACKLIST.equals(tag)) { - passwordBlacklistFile = parser.getAttributeValue(null, ATTR_VALUE); }else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); @@ -1589,8 +1578,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(minimumPasswordMetrics.symbols); pw.print(prefix); pw.print("minimumPasswordNonLetter="); pw.println(minimumPasswordMetrics.nonLetter); - pw.print(prefix); pw.print("passwordBlacklist="); - pw.println(passwordBlacklistFile != null); pw.print(prefix); pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); pw.print(prefix); pw.print("strongAuthUnlockTimeout="); @@ -1857,10 +1844,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return new LockPatternUtils(mContext); } - PasswordBlacklist newPasswordBlacklist(File file) { - return new PasswordBlacklist(file); - } - boolean storageManagerIsFileBasedEncryptionEnabled() { return StorageManager.isFileEncryptedNativeOnly(); } @@ -4413,136 +4396,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - /* @return the password blacklist set by the admin or {@code null} if none. */ - PasswordBlacklist getAdminPasswordBlacklistLocked(@NonNull ActiveAdmin admin) { - final int userId = UserHandle.getUserId(admin.getUid()); - return admin.passwordBlacklistFile == null ? null : new PasswordBlacklist( - new File(getPolicyFileDirectory(userId), admin.passwordBlacklistFile)); - } - - private static final String PASSWORD_BLACKLIST_FILE_PREFIX = "password-blacklist-"; - private static final String PASSWORD_BLACKLIST_FILE_SUFFIX = ""; - - @Override - public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist, - boolean parent) { - if (!mHasFeature) { - return false; - } - Preconditions.checkNotNull(who, "who is null"); - - synchronized (this) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); - final int userId = mInjector.userHandleGetCallingUserId(); - PasswordBlacklist adminBlacklist = getAdminPasswordBlacklistLocked(admin); - - if (blacklist == null || blacklist.isEmpty()) { - // Remove the adminBlacklist - admin.passwordBlacklistFile = null; - saveSettingsLocked(userId); - if (adminBlacklist != null) { - adminBlacklist.delete(); - } - return true; - } - - // Validate server side - Preconditions.checkNotNull(name, "name is null"); - DevicePolicyManager.enforcePasswordBlacklistSize(blacklist); - - // Blacklist is case insensitive so normalize to lower case - final int blacklistSize = blacklist.size(); - for (int i = 0; i < blacklistSize; ++i) { - blacklist.set(i, blacklist.get(i).toLowerCase()); - } - - final boolean isNewBlacklist = adminBlacklist == null; - if (isNewBlacklist) { - // Create a new file for the blacklist. There could be multiple admins, each setting - // different blacklists, to restrict a user's credential, for example a managed - // profile can impose restrictions on its parent while the parent is already - // restricted by its own admin. A deterministic naming scheme would be fragile if - // new types of admin are introduced so we generate and save the file name instead. - // This isn't a temporary file but it reuses the name generation logic - final File file; - try { - file = File.createTempFile(PASSWORD_BLACKLIST_FILE_PREFIX, - PASSWORD_BLACKLIST_FILE_SUFFIX, getPolicyFileDirectory(userId)); - } catch (IOException e) { - Slog.e(LOG_TAG, "Failed to make a file for the blacklist", e); - return false; - } - adminBlacklist = mInjector.newPasswordBlacklist(file); - } - - if (adminBlacklist.savePasswordBlacklist(name, blacklist)) { - if (isNewBlacklist) { - // The blacklist was saved so point the admin to the file - admin.passwordBlacklistFile = adminBlacklist.getFile().getName(); - saveSettingsLocked(userId); - } - return true; - } - } - - return false; - } - - @Override - public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId, - boolean parent) { - if (!mHasFeature) { - return null; - } - Preconditions.checkNotNull(who, "who is null"); - enforceFullCrossUsersPermission(userId); - synchronized (this) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); - final PasswordBlacklist blacklist = getAdminPasswordBlacklistLocked(admin); - if (blacklist == null) { - return null; - } - return blacklist.getName(); - } - } - - @Override - public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) { - if (!mHasFeature) { - return false; - } - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.TEST_BLACKLISTED_PASSWORD, null); - return isPasswordBlacklistedInternal(userId, password); - } - - private boolean isPasswordBlacklistedInternal(@UserIdInt int userId, String password) { - Preconditions.checkNotNull(password, "Password is null"); - enforceFullCrossUsersPermission(userId); - - // Normalize to lower case for case insensitive blacklist match - final String lowerCasePassword = password.toLowerCase(); - - synchronized (this) { - final List<ActiveAdmin> admins = - getActiveAdminsForLockscreenPoliciesLocked(userId, /* parent */ false); - final int N = admins.size(); - for (int i = 0; i < N; i++) { - final PasswordBlacklist blacklist - = getAdminPasswordBlacklistLocked(admins.get(i)); - if (blacklist != null) { - if (blacklist.isPasswordBlacklisted(lowerCasePassword)) { - return true; - } - } - } - } - - return false; - } - @Override public boolean isActivePasswordSufficient(int userHandle, boolean parent) { if (!mHasFeature) { @@ -4938,11 +4791,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } } - - if (isPasswordBlacklistedInternal(userHandle, password)) { - Slog.w(LOG_TAG, "resetPassword: the password is blacklisted"); - return false; - } } DevicePolicyData policy = getUserData(userHandle); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java b/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java deleted file mode 100644 index a17a1075f100..000000000000 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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 com.android.server.devicepolicy; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.AtomicFile; -import android.util.Slog; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.List; - -/** - * Manages the blacklisted passwords. - * - * This caller must ensure synchronized access. - */ -public class PasswordBlacklist { - private static final String TAG = "PasswordBlacklist"; - - private final AtomicFile mFile; - - /** - * Create an object to manage the password blacklist. - * - * This is a lightweight operation to prepare variables but not perform any IO. - */ - public PasswordBlacklist(File file) { - mFile = new AtomicFile(file, "device-policy"); - } - - /** - * Atomically replace the blacklist. - * - * Pass {@code null} for an empty list. - */ - public boolean savePasswordBlacklist(@NonNull String name, @NonNull List<String> blacklist) { - FileOutputStream fos = null; - try { - fos = mFile.startWrite(); - final DataOutputStream out = buildStreamForWriting(fos); - final Header header = new Header(Header.VERSION_1, name, blacklist.size()); - header.write(out); - final int blacklistSize = blacklist.size(); - for (int i = 0; i < blacklistSize; ++i) { - out.writeUTF(blacklist.get(i)); - } - out.flush(); - mFile.finishWrite(fos); - return true; - } catch (IOException e) { - mFile.failWrite(fos); - return false; - } - } - - /** @return the name of the blacklist or {@code null} if none set. */ - public String getName() { - try (DataInputStream in = openForReading()) { - return Header.read(in).mName; - } catch (IOException e) { - Slog.wtf(TAG, "Failed to read blacklist file", e); - } - return null; - } - - /** @return the number of blacklisted passwords. */ - public int getSize() { - final int blacklistSize; - try (DataInputStream in = openForReading()) { - return Header.read(in).mSize; - } catch (IOException e) { - Slog.wtf(TAG, "Failed to read blacklist file", e); - } - return 0; - } - - /** @return whether the password matches an blacklisted item. */ - public boolean isPasswordBlacklisted(@NonNull String password) { - final int blacklistSize; - try (DataInputStream in = openForReading()) { - final Header header = Header.read(in); - for (int i = 0; i < header.mSize; ++i) { - if (in.readUTF().equals(password)) { - return true; - } - } - } catch (IOException e) { - Slog.wtf(TAG, "Failed to read blacklist file", e); - // Fail safe and block all passwords. Setting a new blacklist should resolve this - // problem which can be identified by examining the log. - return true; - } - return false; - } - - /** Delete the blacklist completely from disk. */ - public void delete() { - mFile.delete(); - } - - /** Get the file the blacklist is stored in. */ - public File getFile() { - return mFile.getBaseFile(); - } - - private DataOutputStream buildStreamForWriting(FileOutputStream fos) { - return new DataOutputStream(new BufferedOutputStream(fos)); - } - - private DataInputStream openForReading() throws IOException { - return new DataInputStream(new BufferedInputStream(mFile.openRead())); - } - - /** - * Helper to read and write the header of the blacklist file. - */ - private static class Header { - static final int VERSION_1 = 1; - - final int mVersion; // File format version - final String mName; - final int mSize; - - Header(int version, String name, int size) { - mVersion = version; - mName = name; - mSize = size; - } - - void write(DataOutputStream out) throws IOException { - out.writeInt(mVersion); - out.writeUTF(mName); - out.writeInt(mSize); - } - - static Header read(DataInputStream in) throws IOException { - final int version = in.readInt(); - final String name = in.readUTF(); - final int size = in.readInt(); - return new Header(version, name, size); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 520f31879e35..cd3928558c4b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -98,12 +98,6 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi this.context = injector.context; } - @Override - public boolean isPasswordBlacklisted(int userId, String password) { - return false; - } - - public void notifyChangeToContentObserver(Uri uri, int userHandle) { ContentObserver co = mMockInjector.mContentObservers.get(new Pair<>(uri, userHandle)); if (co != null) { @@ -220,11 +214,6 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override - PasswordBlacklist newPasswordBlacklist(File file) { - return services.passwordBlacklist; - } - - @Override boolean storageManagerIsFileBasedEncryptionEnabled() { return services.storageManager.isFileBasedEncryptionEnabled(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index ccf4a8293d67..b76064b7ebdf 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4192,36 +4192,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpm.clearResetPasswordToken(admin1)); } - public void testSetPasswordBlacklistCannotBeCalledByNonAdmin() throws Exception { - assertExpectException(SecurityException.class, /* messageRegex= */ null, - () -> dpm.setPasswordBlacklist(admin1, null, null)); - verifyZeroInteractions(getServices().passwordBlacklist); - } - - public void testClearingPasswordBlacklistDoesNotCreateNewBlacklist() throws Exception { - setupProfileOwner(); - dpm.setPasswordBlacklist(admin1, null, null); - verifyZeroInteractions(getServices().passwordBlacklist); - } - - public void testSetPasswordBlacklistCreatesNewBlacklist() throws Exception { - final String name = "myblacklist"; - final List<String> explicit = Arrays.asList("password", "letmein"); - setupProfileOwner(); - dpm.setPasswordBlacklist(admin1, name, explicit); - verify(getServices().passwordBlacklist).savePasswordBlacklist(name, explicit); - } - - public void testSetPasswordBlacklistOnlyConvertsExplicitToLowerCase() throws Exception { - final List<String> mixedCase = Arrays.asList("password", "LETMEIN", "FooTBAll"); - final List<String> lowerCase = Arrays.asList("password", "letmein", "football"); - mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; - setupDeviceOwner(); - final String name = "Name of the Blacklist"; - dpm.setPasswordBlacklist(admin1, name, mixedCase); - verify(getServices().passwordBlacklist).savePasswordBlacklist(name, lowerCase); - } - public void testIsActivePasswordSufficient() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; mContext.packageName = admin1.getPackageName(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 81ed6e2588fd..e753df1f30ec 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -96,7 +96,6 @@ public class MockSystemServices { public final IBackupManager ibackupManager; public final IAudioService iaudioService; public final LockPatternUtils lockPatternUtils; - public final PasswordBlacklist passwordBlacklist; public final StorageManagerForMock storageManager; public final WifiManager wifiManager; public final SettingsForMock settings; @@ -135,7 +134,6 @@ public class MockSystemServices { ibackupManager = mock(IBackupManager.class); iaudioService = mock(IAudioService.class); lockPatternUtils = mock(LockPatternUtils.class); - passwordBlacklist = mock(PasswordBlacklist.class); storageManager = mock(StorageManagerForMock.class); wifiManager = mock(WifiManager.class); settings = mock(SettingsForMock.class); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java deleted file mode 100644 index 1b3fc2c1f207..000000000000 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 com.android.server.devicepolicy; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; - -/** - * Unit tests for {@link PasswordBlacklist}. - * - * bit FrameworksServicesTests:com.android.server.devicepolicy.PasswordBlacklistTest - * runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java - */ -@RunWith(AndroidJUnit4.class) -public final class PasswordBlacklistTest { - private File mBlacklistFile; - private PasswordBlacklist mBlacklist; - - @Before - public void setUp() throws IOException { - mBlacklistFile = File.createTempFile("pwdbl", null); - mBlacklist = new PasswordBlacklist(mBlacklistFile); - } - - @After - public void tearDown() { - mBlacklist.delete(); - } - - @Test - public void matchIsExact() { - // Note: Case sensitivity is handled by the user of PasswordBlacklist by normalizing the - // values stored in and tested against it. - mBlacklist.savePasswordBlacklist("matchIsExact", Arrays.asList("password", "qWERty")); - assertTrue(mBlacklist.isPasswordBlacklisted("password")); - assertTrue(mBlacklist.isPasswordBlacklisted("qWERty")); - assertFalse(mBlacklist.isPasswordBlacklisted("Password")); - assertFalse(mBlacklist.isPasswordBlacklisted("qwert")); - assertFalse(mBlacklist.isPasswordBlacklisted("letmein")); - } - - @Test - public void matchIsNotRegex() { - mBlacklist.savePasswordBlacklist("matchIsNotRegex", Arrays.asList("a+b*")); - assertTrue(mBlacklist.isPasswordBlacklisted("a+b*")); - assertFalse(mBlacklist.isPasswordBlacklisted("aaaa")); - assertFalse(mBlacklist.isPasswordBlacklisted("abbbb")); - assertFalse(mBlacklist.isPasswordBlacklisted("aaaa")); - } - - @Test - public void matchFailsSafe() throws IOException { - try (FileOutputStream fos = new FileOutputStream(mBlacklistFile)) { - // Write a malformed blacklist file - fos.write(17); - } - assertTrue(mBlacklist.isPasswordBlacklisted("anything")); - assertTrue(mBlacklist.isPasswordBlacklisted("at")); - assertTrue(mBlacklist.isPasswordBlacklisted("ALL")); - } - - @Test - public void blacklistCanBeNamed() { - final String name = "identifier"; - mBlacklist.savePasswordBlacklist(name, Arrays.asList("one", "two", "three")); - assertEquals(mBlacklist.getName(), name); - } - - @Test - public void reportsTheCorrectNumberOfEntries() { - mBlacklist.savePasswordBlacklist("Count Entries", Arrays.asList("1", "2", "3", "4")); - assertEquals(mBlacklist.getSize(), 4); - } - - @Test - public void reportsBlacklistFile() { - assertEquals(mBlacklistFile, mBlacklist.getFile()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index a98e29137fb6..f4ec867333ad 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -211,24 +211,6 @@ public class RecoverableKeyStoreManagerTest { } @Test - public void generateAndStoreKey_storesTheKey() throws Exception { - int uid = Binder.getCallingUid(); - int userId = UserHandle.getCallingUserId(); - - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); - - assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); - - assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); - } - - @Test - public void generateAndStoreKey_returnsAKeyOfAppropriateSize() throws Exception { - assertThat(mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS)) - .hasLength(RECOVERABLE_KEY_SIZE_BYTES); - } - - @Test public void importKey_storesTheKey() throws Exception { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); @@ -265,7 +247,7 @@ public class RecoverableKeyStoreManagerTest { @Test public void removeKey_removesAKey() throws Exception { int uid = Binder.getCallingUid(); - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); @@ -276,7 +258,7 @@ public class RecoverableKeyStoreManagerTest { public void removeKey_updatesShouldCreateSnapshot() throws Exception { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); // Pretend that key was synced mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); @@ -1056,7 +1038,7 @@ public class RecoverableKeyStoreManagerTest { int userId = UserHandle.getCallingUserId(); mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 }); - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); // Pretend that key was synced mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 }); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java index 36136a8932c9..ce7445788489 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java @@ -206,6 +206,31 @@ public class ScheduleCalendarTest extends UiServiceTestCase { } @Test + public void testShouldExitForAlarm_oldAlarm() { + // Cal: today 2:15pm + Calendar cal = new GregorianCalendar(); + cal.set(Calendar.HOUR_OF_DAY, 14); + cal.set(Calendar.MINUTE, 15); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + // ScheduleInfo: today 12:16pm - today 3:15pm + mScheduleInfo.days = new int[] {getTodayDay()}; + mScheduleInfo.startHour = 12; + mScheduleInfo.endHour = 3; + mScheduleInfo.startMinute = 16; + mScheduleInfo.endMinute = 15; + mScheduleInfo.exitAtAlarm = true; + mScheduleInfo.nextAlarm = 1000; // very old alarm + + mScheduleCalendar.setSchedule(mScheduleInfo); + assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis())); + + // don't exit for an alarm if it's an old alarm + assertFalse(mScheduleCalendar.shouldExitForAlarm(1000)); + } + + @Test public void testMaybeSetNextAlarm_settingOff() { mScheduleInfo.exitAtAlarm = false; mScheduleInfo.nextAlarm = 0; diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java index 92ebf367025c..dbad02fd5640 100644 --- a/telephony/java/android/telephony/NetworkServiceCallback.java +++ b/telephony/java/android/telephony/NetworkServiceCallback.java @@ -83,6 +83,8 @@ public class NetworkServiceCallback { } catch (RemoteException e) { Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote"); } + } else { + Rlog.e(mTag, "Weak reference of callback is null."); } } }
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java index 6d7218179067..283112793d87 100644 --- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java +++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java @@ -29,27 +29,47 @@ import android.os.Parcelable; public final class ImsCallForwardInfo implements Parcelable { // Refer to ImsUtInterface#CDIV_CF_XXX /** @hide */ + // TODO: Make private, do not modify this field directly, use getter. public int mCondition; // 0: disabled, 1: enabled /** @hide */ + // TODO: Make private, do not modify this field directly, use getter. public int mStatus; // 0x91: International, 0x81: Unknown /** @hide */ + // TODO: Make private, do not modify this field directly, use getter. public int mToA; // Service class /** @hide */ + // TODO: Make private, do not modify this field directly, use getter. public int mServiceClass; // Number (it will not include the "sip" or "tel" URI scheme) /** @hide */ + // TODO: Make private, do not modify this field directly, use getter. public String mNumber; // No reply timer for CF /** @hide */ + // TODO: Make private, do not modify this field directly, use getter. public int mTimeSeconds; /** @hide */ + // TODO: Will be removed in the future, use public constructor instead. public ImsCallForwardInfo() { } + /** + * IMS Call Forward Information. + */ + public ImsCallForwardInfo(int condition, int status, int toA, int serviceClass, String number, + int replyTimerSec) { + mCondition = condition; + mStatus = status; + mToA = toA; + mServiceClass = serviceClass; + mNumber = number; + mTimeSeconds = replyTimerSec; + } + /** @hide */ public ImsCallForwardInfo(Parcel in) { readFromParcel(in); diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index 1ddf1994f26b..49ead770b42a 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -15,19 +15,24 @@ */ package android.telephony.ims; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** - * Provided STK Call Control Suplementary Service information + * Provides STK Call Control Supplementary Service information. * * {@hide} */ @SystemApi public final class ImsSsData implements Parcelable { - //ServiceType + // Supplementary Service Type + // Call Forwarding public static final int SS_CFU = 0; public static final int SS_CF_BUSY = 1; public static final int SS_CF_NO_REPLY = 2; @@ -35,12 +40,16 @@ public final class ImsSsData implements Parcelable { public static final int SS_CF_ALL = 4; public static final int SS_CF_ALL_CONDITIONAL = 5; public static final int SS_CFUT = 6; + // Called Line Presentation public static final int SS_CLIP = 7; public static final int SS_CLIR = 8; public static final int SS_COLP = 9; public static final int SS_COLR = 10; + // Calling Name Presentation public static final int SS_CNAP = 11; + // Call Waiting public static final int SS_WAIT = 12; + // Call Barring public static final int SS_BAOC = 13; public static final int SS_BAOIC = 14; public static final int SS_BAOIC_EXC_HOME = 15; @@ -52,14 +61,14 @@ public final class ImsSsData implements Parcelable { public static final int SS_INCOMING_BARRING_DN = 21; public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; - //SSRequestType + //Supplementary Service Request Types public static final int SS_ACTIVATION = 0; public static final int SS_DEACTIVATION = 1; public static final int SS_INTERROGATION = 2; public static final int SS_REGISTRATION = 3; public static final int SS_ERASURE = 4; - //TeleserviceType + // Supplementary Service Teleservice Type public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; public static final int SS_ALL_TELESEVICES = 1; public static final int SS_TELEPHONY = 2; @@ -67,40 +76,226 @@ public final class ImsSsData implements Parcelable { public static final int SS_SMS_SERVICES = 4; public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; - // Refer to ServiceType + // Service Class of Supplementary Service + // See 27.007 +CCFC or +CLCK /** @hide */ - public int serviceType; - // Refere to SSRequestType + public static final int SERVICE_CLASS_NONE = 0; // no user input /** @hide */ - public int requestType; - // Refer to TeleserviceType + public static final int SERVICE_CLASS_VOICE = 1; /** @hide */ - public int teleserviceType; - // Service Class + public static final int SERVICE_CLASS_DATA = (1 << 1); /** @hide */ - public int serviceClass; - // Error information + public static final int SERVICE_CLASS_FAX = (1 << 2); /** @hide */ - public int result; - + public static final int SERVICE_CLASS_SMS = (1 << 3); + /** @hide */ + public static final int SERVICE_CLASS_DATA_SYNC = (1 << 4); + /** @hide */ + public static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5); /** @hide */ - public int[] ssInfo; /* Valid for all supplementary services. - This field will be empty for RequestType SS_INTERROGATION - and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN, - SS_INCOMING_BARRING_ANONYMOUS.*/ + public static final int SERVICE_CLASS_PACKET = (1 << 6); + /** @hide */ + public static final int SERVICE_CLASS_PAD = (1 << 7); + + /** + * Result code used if the operation was successful. See {@link #result}. + * @hide + */ + public static final int RESULT_SUCCESS = 0; /** @hide */ - public ImsCallForwardInfo[] cfInfo; /* Valid only for supplementary services - ServiceType SS_CF_* and RequestType SS_INTERROGATION */ + @IntDef(flag = true, prefix = { "SS_" }, value = { + SS_CFU, + SS_CF_BUSY, + SS_CF_NO_REPLY, + SS_CF_NOT_REACHABLE, + SS_CF_ALL, + SS_CF_ALL_CONDITIONAL, + SS_CFUT, + SS_CLIP, + SS_CLIR, + SS_COLP, + SS_COLR, + SS_CNAP, + SS_WAIT, + SS_BAOC, + SS_BAOIC, + SS_BAOIC_EXC_HOME, + SS_BAIC, + SS_BAIC_ROAMING, + SS_ALL_BARRING, + SS_OUTGOING_BARRING, + SS_INCOMING_BARRING, + SS_INCOMING_BARRING_DN, + SS_INCOMING_BARRING_ANONYMOUS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ServiceType{} /** @hide */ - public ImsSsInfo[] imsSsInfo; /* Valid only for ServiceType SS_INCOMING_BARRING_DN and - ServiceType SS_INCOMING_BARRING_ANONYMOUS */ + @IntDef(flag = true, prefix = { "SERVICE_CLASS" }, value = { + SERVICE_CLASS_NONE, + SERVICE_CLASS_VOICE, + SERVICE_CLASS_DATA, + SERVICE_CLASS_FAX, + SERVICE_CLASS_SMS, + SERVICE_CLASS_DATA_SYNC, + SERVICE_CLASS_DATA_ASYNC, + SERVICE_CLASS_PACKET, + SERVICE_CLASS_PAD + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ServiceClass{} + + /** + * The Service type of this Supplementary service. Valid values include: + * SS_CFU, + * SS_CF_BUSY, + * SS_CF_NO_REPLY, + * SS_CF_NOT_REACHABLE, + * SS_CF_ALL, + * SS_CF_ALL_CONDITIONAL, + * SS_CFUT, + * SS_CLIP, + * SS_CLIR, + * SS_COLP, + * SS_COLR, + * SS_CNAP, + * SS_WAIT, + * SS_BAOC, + * SS_BAOIC, + * SS_BAOIC_EXC_HOME, + * SS_BAIC, + * SS_BAIC_ROAMING, + * SS_ALL_BARRING, + * SS_OUTGOING_BARRING, + * SS_INCOMING_BARRING, + * SS_INCOMING_BARRING_DN, + * SS_INCOMING_BARRING_ANONYMOUS + * + * @hide + */ + // TODO: Make final, do not modify this field directly! + public int serviceType; + + /** + * Supplementary Service request Type. Valid values are: + * SS_ACTIVATION, + * SS_DEACTIVATION, + * SS_INTERROGATION, + * SS_REGISTRATION, + * SS_ERASURE + * + * @hide + */ + // TODO: Make final, do not modify this field directly! + public int requestType; + + /** + * Supplementary Service teleservice type: + * SS_TELESERVICE_ALL_TELE_AND_BEARER, + * SS_TELESERVICE_ALL_TELESEVICES, + * SS_TELESERVICE_TELEPHONY, + * SS_TELESERVICE_ALL_DATA, + * SS_TELESERVICE_SMS, + * SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS + * + * @hide + */ + // TODO: Make this param final! Do not try to modify this param directly. + public int teleserviceType; + + /** + * Supplementary Service service class. Valid values are: + * SERVICE_CLASS_NONE, + * SERVICE_CLASS_VOICE, + * SERVICE_CLASS_DATA, + * SERVICE_CLASS_FAX, + * SERVICE_CLASS_SMS, + * SERVICE_CLASS_DATA_SYNC, + * SERVICE_CLASS_DATA_ASYNC, + * SERVICE_CLASS_PACKET, + * SERVICE_CLASS_PAD + * + * @hide + */ + // TODO: Make this param final! Do not try to modify this param directly. + public int serviceClass; + + /** + * Result of Supplementary Service operation. Valid values are: + * RESULT_SUCCESS if the result is success, or + * ImsReasonInfo code if the result is a failure. + * + * @hide + */ + // TODO: Make this param final! Do not try to modify this param directly. + public final int result; + + private int[] mSsInfo; + private ImsCallForwardInfo[] mCfInfo; + private ImsSsInfo[] mImsSsInfo; - public ImsSsData() {} + /** + * Generate IMS Supplementary Service information. + * @param serviceType The Supplementary Service type. Valid entries: + * SS_CFU, + * SS_CF_BUSY, + * SS_CF_NO_REPLY, + * SS_CF_NOT_REACHABLE, + * SS_CF_ALL, + * SS_CF_ALL_CONDITIONAL, + * SS_CFUT, + * SS_CLIP, + * SS_CLIR, + * SS_COLP, + * SS_COLR, + * SS_CNAP, + * SS_WAIT, + * SS_BAOC, + * SS_BAOIC, + * SS_BAOIC_EXC_HOME, + * SS_BAIC, + * SS_BAIC_ROAMING, + * SS_ALL_BARRING, + * SS_OUTGOING_BARRING, + * SS_INCOMING_BARRING, + * SS_INCOMING_BARRING_DN, + * SS_INCOMING_BARRING_ANONYMOUS + * @param requestType Supplementary Service request Type. Valid values are: + * SS_ACTIVATION, + * SS_DEACTIVATION, + * SS_INTERROGATION, + * SS_REGISTRATION, + * SS_ERASURE + * @param teleserviceType Supplementary Service teleservice type: + * SS_TELESERVICE_ALL_TELE_AND_BEARER, + * SS_TELESERVICE_ALL_TELESEVICES, + * SS_TELESERVICE_TELEPHONY, + * SS_TELESERVICE_ALL_DATA, + * SS_TELESERVICE_SMS, + * SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS + * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK. + * @param result Result of Supplementary Service operation. Valid values are 0 if the result is + * success, or ImsReasonInfo code if the result is a failure. + */ + public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType, + @ServiceClass int serviceClass, int result) { + this.serviceType = serviceType; + this.requestType = requestType; + this.teleserviceType = teleserviceType; + this.serviceClass = serviceClass; + this.result = result; + } private ImsSsData(Parcel in) { - readFromParcel(in); + serviceType = in.readInt(); + requestType = in.readInt(); + teleserviceType = in.readInt(); + serviceClass = in.readInt(); + result = in.readInt(); + mSsInfo = in.createIntArray(); + mCfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader()); } public static final Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() { @@ -122,18 +317,8 @@ public final class ImsSsData implements Parcelable { out.writeInt(teleserviceType); out.writeInt(serviceClass); out.writeInt(result); - out.writeIntArray(ssInfo); - out.writeParcelableArray(cfInfo, 0); - } - - private void readFromParcel(Parcel in) { - serviceType = in.readInt(); - requestType = in.readInt(); - teleserviceType = in.readInt(); - serviceClass = in.readInt(); - result = in.readInt(); - ssInfo = in.createIntArray(); - cfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader()); + out.writeIntArray(mSsInfo); + out.writeParcelableArray(mCfInfo, 0); } @Override @@ -200,7 +385,55 @@ public final class ImsSsData implements Parcelable { } public boolean isTypeInterrogation() { - return (requestType == SS_INTERROGATION); + return (serviceType == SS_INTERROGATION); + } + + /** @hide */ + public void setSuppServiceInfo(int[] ssInfo) { + mSsInfo = ssInfo; + } + + /** @hide */ + public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) { + mImsSsInfo = imsSsInfo; + } + + /** @hide */ + public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) { + mCfInfo = cfInfo; + } + + /** + * This field will be null for RequestType SS_INTERROGATION + * and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN, + * SS_INCOMING_BARRING_ANONYMOUS. + * + * @hide + */ + public int[] getSuppServiceInfo() { + return mSsInfo; + } + + /** + * Valid only for ServiceTypes + * - SS_INCOMING_BARRING_DN and + * - ServiceType SS_INCOMING_BARRING_ANONYMOUS. + * Will be null otherwise. + * @hide + */ + public ImsSsInfo[] getImsSpecificSuppServiceInfo() { + return mImsSsInfo; + } + + /** + * Valid only for supplementary services + * - ServiceType SS_CF_* and + * - RequestType SS_INTERROGATION. + * Will be null otherwise. + * @hide + **/ + public ImsCallForwardInfo[] getCallForwardInfo() { + return mCfInfo; } public String toString() { diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java index 1d1292fb9f72..c6f8622f3fd9 100644 --- a/telephony/java/android/telephony/ims/ImsSsInfo.java +++ b/telephony/java/android/telephony/ims/ImsSsInfo.java @@ -36,13 +36,31 @@ public final class ImsSsInfo implements Parcelable { // 0: disabled, 1: enabled /** @hide */ + // TODO: Make private, do not modify this field directly, use getter! public int mStatus; /** @hide */ + // TODO: Make private, do not modify this field directly, use getter! public String mIcbNum; + /**@hide*/ + // TODO: Remove! Do not use this constructor, instead use public version. public ImsSsInfo() { } + /** + * + * @param status The status of the service registration of activation/deactiviation. Valid + * entries include: + * {@link #NOT_REGISTERED}, + * {@link #DISABLED}, + * {@link #ENABLED} + * @param icbNum The Incoming barring number. + */ + public ImsSsInfo(int status, String icbNum) { + mStatus = status; + mIcbNum = icbNum; + } + private ImsSsInfo(Parcel in) { readFromParcel(in); } @@ -81,6 +99,12 @@ public final class ImsSsInfo implements Parcelable { } }; + /** + * @return Supplementary Service Configuration status. Valid Values are: + * {@link #NOT_REGISTERED}, + * {@link #DISABLED}, + * {@link #ENABLED} + */ public int getStatus() { return mStatus; } diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index 3734412981d8..e247951f16ef 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -173,7 +173,7 @@ public final class BackgroundDexOptServiceIntegrationTests { private static String getCompilerFilter(String pkg) throws IOException { String cmd = String.format("dumpsys package %s", pkg); String[] lines = runShellCommandSplitLines(cmd); - final String substr = "compilation_filter="; + final String substr = "[status="; for (String line : lines) { int startIndex = line.indexOf(substr); if (startIndex < 0) { diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java index 6bdfdc6db2d1..85e836179b5e 100644 --- a/tests/net/java/android/net/IpSecAlgorithmTest.java +++ b/tests/net/java/android/net/IpSecAlgorithmTest.java @@ -22,8 +22,12 @@ import static org.junit.Assert.fail; import android.os.Parcel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; + +import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; +import java.util.Map.Entry; import java.util.Random; + import org.junit.Test; import org.junit.runner.RunWith; @@ -40,19 +44,29 @@ public class IpSecAlgorithmTest { }; @Test - public void testDefaultTruncLen() throws Exception { - IpSecAlgorithm explicit = - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256); - IpSecAlgorithm implicit = + public void testNoTruncLen() throws Exception { + Entry<String, Integer>[] authAndAeadList = + new Entry[] { + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512), + new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224) + }; + + // Expect auth and aead algorithms to throw errors if trunclen is omitted. + for (Entry<String, Integer> algData : authAndAeadList) { + try { new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8)); - assertTrue( - "Default Truncation Length Incorrect, Explicit: " - + explicit - + "implicit: " - + implicit, - IpSecAlgorithm.equals(explicit, implicit)); + algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8)); + fail("Expected exception on unprovided auth trunclen"); + } catch (IllegalArgumentException expected) { + } + } + + // Ensure crypt works with no truncation length supplied. + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8)); } @Test diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java index f186ee55d2c7..771faaf4955d 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -62,7 +62,8 @@ public class IpSecConfigTest { c.setAuthentication( new IpSecAlgorithm( IpSecAlgorithm.AUTH_HMAC_MD5, - new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0})); + new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}, + 128)); c.setAuthenticatedEncryption( new IpSecAlgorithm( IpSecAlgorithm.AUTH_CRYPT_AES_GCM, diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java index 0ca20dee427f..970596d6b0f3 100644 --- a/tests/net/java/android/net/IpSecManagerTest.java +++ b/tests/net/java/android/net/IpSecManagerTest.java @@ -179,7 +179,7 @@ public class IpSecManagerTest { IpSecManager.UdpEncapsulationSocket encapSocket = mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT); - assertNotNull(encapSocket.getSocket()); + assertNotNull(encapSocket.getFileDescriptor()); assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); encapSocket.close(); @@ -202,7 +202,7 @@ public class IpSecManagerTest { IpSecManager.UdpEncapsulationSocket encapSocket = mIpSecManager.openUdpEncapsulationSocket(); - assertNotNull(encapSocket.getSocket()); + assertNotNull(encapSocket.getFileDescriptor()); assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); encapSocket.close(); |