Merge "Update QS edit layout." into pi-dev
diff --git a/Android.mk b/Android.mk
index e219661..d7d9c90 100644
--- a/Android.mk
+++ b/Android.mk
@@ -252,6 +252,11 @@
-federate SupportLib https://developer.android.com \
-federationapi SupportLib prebuilts/sdk/current/support-api.txt
+# Federate AndroidX references against local API file.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+ -federate AndroidX https://developer.android.com \
+ -federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
+
# ==== Public API diff ===========================
include $(CLEAR_VARS)
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
new file mode 100644
index 0000000..6aa34a6
--- /dev/null
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<configuration description="Runs CorePerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CorePerfTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.core" />
+ </test>
+</configuration>
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index a803369..9bc7d05 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -26,6 +26,8 @@
LOCAL_PACKAGE_NAME := MultiUserPerfTests
LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE += device-tests
+
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
new file mode 100644
index 0000000..6ede827
--- /dev/null
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<configuration description="Runs MultiUserPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="MultiUserPerfTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.multiuser" />
+ </test>
+</configuration>
diff --git a/api/current.txt b/api/current.txt
index 406ebac..8090aa0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6115,8 +6115,12 @@
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+ method public void grantRuntimePermission(java.lang.String, java.lang.String);
+ method public void grantRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean injectInputEvent(android.view.InputEvent, boolean);
method public boolean performGlobalAction(int);
+ method public void revokeRuntimePermission(java.lang.String, java.lang.String);
+ method public void revokeRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
method public void setRunAsMonkey(boolean);
@@ -6464,7 +6468,6 @@
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);
@@ -6501,7 +6504,7 @@
method public boolean installExistingPackage(android.content.ComponentName, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
- method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean, boolean);
+ method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isAffiliatedUser();
@@ -6573,7 +6576,6 @@
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);
@@ -6685,6 +6687,8 @@
field public static final int ID_TYPE_IMEI = 4; // 0x4
field public static final int ID_TYPE_MEID = 8; // 0x8
field public static final int ID_TYPE_SERIAL = 2; // 0x2
+ field public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1; // 0x1
+ field public static final int INSTALLKEY_SET_USER_SELECTABLE = 2; // 0x2
field public static final int KEYGUARD_DISABLE_BIOMETRICS = 416; // 0x1a0
field public static final int KEYGUARD_DISABLE_FACE = 128; // 0x80
field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
@@ -7213,6 +7217,7 @@
field public static final java.lang.String HINT_LIST_ITEM = "list_item";
field public static final java.lang.String HINT_NO_TINT = "no_tint";
field public static final java.lang.String HINT_PARTIAL = "partial";
+ field public static final java.lang.String HINT_PERMISSION_REQUEST = "permission_request";
field public static final java.lang.String HINT_SEE_MORE = "see_more";
field public static final java.lang.String HINT_SELECTED = "selected";
field public static final java.lang.String HINT_SHORTCUT = "shortcut";
@@ -7299,6 +7304,7 @@
}
public abstract class SliceProvider extends android.content.ContentProvider {
+ ctor public SliceProvider(java.lang.String...);
ctor public SliceProvider();
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
@@ -13639,22 +13645,22 @@
method public int getAllocator();
method public boolean getConserveMemory();
method public android.graphics.Rect getCrop();
- method public boolean getDecodeAsAlphaMask();
- method public boolean getMutable();
method public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener();
method public android.graphics.PostProcessor getPostProcessor();
- method public boolean getRequireUnpremultiplied();
- method public android.util.Size getSampledSize(int);
- 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 setDecodeAsAlphaMask(boolean);
- method public android.graphics.ImageDecoder setMutable(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 setRequireUnpremultiplied(boolean);
- method public android.graphics.ImageDecoder setResize(int, int);
- method public android.graphics.ImageDecoder setResize(int);
+ method public boolean isDecodeAsAlphaMaskEnabled();
+ method public boolean isMutableRequired();
+ method public boolean isUnpremultipliedRequired();
+ 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
@@ -13670,6 +13676,7 @@
}
public static class ImageDecoder.ImageInfo {
+ method public android.graphics.ColorSpace getColorSpace();
method public java.lang.String getMimeType();
method public android.util.Size getSize();
method public boolean isAnimated();
@@ -21957,6 +21964,7 @@
}
public final class AudioDeviceInfo {
+ method public java.lang.String getAddress();
method public int[] getChannelCounts();
method public int[] getChannelIndexMasks();
method public int[] getChannelMasks();
@@ -24004,6 +24012,7 @@
field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+ field public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; // 0x2
field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
@@ -24447,6 +24456,7 @@
}
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();
@@ -27003,8 +27013,8 @@
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 {
@@ -27133,6 +27143,7 @@
method public void bindSocket(java.net.Socket) throws java.io.IOException;
method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
method public int describeContents();
+ method public static android.net.Network fromNetworkHandle(long);
method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
method public long getNetworkHandle();
@@ -27232,6 +27243,8 @@
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;
}
@@ -38554,6 +38567,7 @@
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();
@@ -38581,6 +38595,7 @@
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);
@@ -38673,6 +38688,7 @@
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();
@@ -38692,6 +38708,7 @@
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);
@@ -50412,9 +50429,9 @@
ctor public TextClassification.Options();
method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
- method public java.util.Calendar getReferenceTime();
+ method public java.time.ZonedDateTime getReferenceTime();
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
- method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.util.Calendar);
+ method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.time.ZonedDateTime);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR;
}
diff --git a/api/removed.txt b/api/removed.txt
index 9fe25c9..2d76c5a 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -183,7 +183,15 @@
public final class ImageDecoder implements java.lang.AutoCloseable {
method public deprecated boolean getAsAlphaMask();
+ method public deprecated boolean getDecodeAsAlphaMask();
+ method public deprecated boolean getMutable();
+ method public deprecated boolean getRequireUnpremultiplied();
method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+ method public deprecated android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean);
+ method public deprecated android.graphics.ImageDecoder setMutable(boolean);
+ method public deprecated android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
+ method public deprecated android.graphics.ImageDecoder setResize(int, int);
+ method public deprecated android.graphics.ImageDecoder setResize(int);
field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 5568dba..1284de88 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -57,6 +57,7 @@
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+ field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
field public static final java.lang.String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
field public static final java.lang.String CONTROL_VPN = "android.permission.CONTROL_VPN";
@@ -1252,6 +1253,7 @@
method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
method public android.graphics.Point getStableDisplaySize();
method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+ method public void setSaturationLevel(float);
}
}
@@ -2101,6 +2103,7 @@
method public java.lang.String getVersion();
method public boolean isBackgroundScanningSupported();
method public boolean isCaptureSupported();
+ method public boolean isInitializationRequired();
method public boolean isProgramIdentifierSupported(int);
method public boolean isProgramTypeSupported(int);
method public void writeToParcel(android.os.Parcel, int);
@@ -3102,6 +3105,10 @@
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();
diff --git a/api/test-current.txt b/api/test-current.txt
index d323725..b520dfb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,8 +1,10 @@
package android {
public static final class Manifest.permission {
+ field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+ field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
}
}
@@ -127,8 +129,8 @@
public final class UiAutomation {
method public void destroy();
method public android.os.ParcelFileDescriptor[] executeShellCommandRw(java.lang.String);
- method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
- method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public deprecated boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public deprecated boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
}
public class UiModeManager {
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 2b85ec0..c6e561f 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -34,11 +34,11 @@
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 @@
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 @@
return NO_ERROR;
}
-status_t FdBuffer::readFully(unique_fd* fd) {
+status_t FdBuffer::readFully(int fd) {
mStartTime = uptimeMillis();
while (true) {
@@ -99,10 +99,10 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
// 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 db3a74b..f467da8 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -40,13 +40,13 @@
* 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 @@
*
* 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 aeccefd..d02b4dd 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -352,8 +352,7 @@
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 3b57d34..3f693fa 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -264,8 +264,9 @@
}
// 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 @@
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 @@
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 @@
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 @@
}
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 @@
// 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 bf77017..7a05d7e 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -37,7 +37,6 @@
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 @@
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 @@
protected:
FdBuffer buffer;
TemporaryFile tf;
- unique_fd tffd;
Fpipe p2cPipe;
Fpipe c2pPipe;
@@ -87,7 +85,7 @@
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, 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 @@
} 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 5edc0c7..10c2981 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -38,7 +38,7 @@
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 @@
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 @@
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 f93839b..2f6698b 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 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 @@
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/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
index b76d669..37b7acf 100644
--- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
+++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
@@ -29,23 +29,21 @@
private String[] mArgs;
private int mNextArg;
private String mCurArgData;
- private boolean mIsForegroundRequest;
+
+ private int mExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
enum Operation {
REQUEST_SYNC {
@Override
void invoke(RequestSync caller) {
- if (caller.mIsForegroundRequest) {
- caller.mExtras.putBoolean(
- ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC, true);
- } else {
- caller.mExtras.putBoolean(
- ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC, true);
+ final int flag = caller.mExemptionFlag;
+ caller.mExtras.putInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, flag);
+ if (flag == ContentResolver.SYNC_EXEMPTION_NONE) {
System.out.println(
"Making a sync request as a background app.\n"
+ "Note: request may be throttled by App Standby.\n"
+ "To override this behavior and run a sync immediately,"
- + " pass a -f option.\n");
+ + " pass a -f or -F option (use -h for help).\n");
}
final SyncRequest request =
new SyncRequest.Builder()
@@ -213,7 +211,10 @@
mExtras.putBoolean(key, Boolean.valueOf(value));
} else if (opt.equals("-f") || opt.equals("--foreground")) {
- mIsForegroundRequest = true;
+ mExemptionFlag = ContentResolver.SYNC_EXEMPTION_ACTIVE;
+
+ } else if (opt.equals("-F") || opt.equals("--top")) {
+ mExemptionFlag = ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP;
} else {
System.err.println("Error: Unknown option: " + opt);
@@ -293,7 +294,9 @@
" -a|--authority <AUTHORITY>\n" +
" App-standby related options\n" +
"\n" +
- " -f|--foreground (Exempt a sync from app standby)\n" +
+ " -f|--foreground (cause WORKING_SET, FREQUENT sync adapters" +
+ " to run immediately)\n" +
+ " -F|--top (cause even RARE sync adapters to run immediately)\n" +
" ContentResolver extra options:\n" +
" --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
" --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index b67764b..50b05cd 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -127,25 +127,25 @@
}
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> 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/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a458c07..c1ff275 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -79,8 +79,6 @@
mSendBroadcast(sendBroadcast),
mTimeBaseSec(timeBaseSec),
mLastLogTimestamp(0) {
- StatsPullerManager statsPullerManager;
- statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -177,7 +175,7 @@
uint64_t curTimeSec = getElapsedRealtimeSec();
if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
- mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec);
+ mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
mLastPullerCacheClearTimeSec = curTimeSec;
}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b03b4b4..9f70c75 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -595,7 +595,7 @@
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
- if (mStatsPullerManager.Pull(s, &stats)) {
+ if (mStatsPullerManager.Pull(s, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5e75359..7fe8e62 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,30 +228,27 @@
* 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;
}
/**
* Logs when the ble scan state changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * 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 @@
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 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 @@
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 @@
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 @@
* 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 @@
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 @@
* 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 @@
* 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 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 @@
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];
- // Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
- optional string tag = 2;
+ 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;
+}
- // 1 indicates is instant app. -1 indicates Not applicable.
- optional sint32 is_instant_app = 5;
+/**
+ * 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];
- // The activity name if available.
- optional string activity_name = 6;
+ optional string tag = 2;
- // The package name if available.
- optional string package_name = 7;
+ // The name of the process.
+ // system_server if it is not by an app
+ optional string process_name = 3;
- // 1 indicates in foreground. -1 indicates not available.
- optional sint32 is_foreground = 8;
+ // The pid if available. -1 means not available.
+ optional sint32 pid = 4;
+}
+
+/**
+ * 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];
+
+ optional string process_name = 2;
+
+ optional string short_component_name = 3;
+
+ optional string reason = 4;
}
/*
@@ -1299,7 +1308,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 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 @@
// 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 @@
/**
* 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/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 3b0cd34..ec5a5d6 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -35,26 +35,31 @@
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
- mCoolDownSec = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownSec;
- VLOG("Puller for tag %d created. Cooldown set to %ld", mTagId, mCoolDownSec);
+ mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
+ VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
}
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
lock_guard<std::mutex> lock(mLock);
+ int64_t wallClockTimeNs = getWallClockNs();
StatsdStats::getInstance().notePull(mTagId);
- long curTime = getElapsedRealtimeSec();
- if (curTime - mLastPullTimeSec < mCoolDownSec) {
+ if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
(*data) = mCachedData;
StatsdStats::getInstance().notePullFromCache(mTagId);
return true;
}
- if (mMinPullIntervalSec > curTime - mLastPullTimeSec) {
- mMinPullIntervalSec = curTime - mLastPullTimeSec;
- StatsdStats::getInstance().updateMinPullIntervalSec(mTagId, mMinPullIntervalSec);
+ if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
+ mMinPullIntervalNs = elapsedTimeNs - mLastPullTimeNs;
+ StatsdStats::getInstance().updateMinPullIntervalSec(mTagId,
+ mMinPullIntervalNs / NS_PER_SEC);
}
mCachedData.clear();
- mLastPullTimeSec = curTime;
+ mLastPullTimeNs = elapsedTimeNs;
bool ret = PullInternal(&mCachedData);
+ for (const shared_ptr<LogEvent>& data : mCachedData) {
+ data->setElapsedTimestampNs(elapsedTimeNs);
+ data->setLogdWallClockTimestampNs(wallClockTimeNs);
+ }
if (ret) {
mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
(*data) = mCachedData;
@@ -70,12 +75,12 @@
lock_guard<std::mutex> lock(mLock);
int ret = mCachedData.size();
mCachedData.clear();
- mLastPullTimeSec = 0;
+ mLastPullTimeNs = 0;
return ret;
}
-int StatsPuller::ClearCacheIfNecessary(long timestampSec) {
- if (timestampSec - mLastPullTimeSec > mCoolDownSec) {
+int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
+ if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
return clearCache();
} else {
return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 936c47e..caac677 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -37,13 +37,13 @@
virtual ~StatsPuller() {}
- bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
// Clear cache if elapsed time is more than cooldown time
- int ClearCacheIfNecessary(long timestampSec);
+ int ClearCacheIfNecessary(int64_t timestampNs);
static void SetUidMap(const sp<UidMap>& uidMap);
@@ -59,9 +59,9 @@
// If a pull request comes before cooldown, a cached version from purevious pull
// will be returned.
// The actual value should be determined by individual pullers.
- long mCoolDownSec;
+ int64_t mCoolDownNs;
// For puller stats
- long mMinPullIntervalSec = LONG_MAX;
+ int64_t mMinPullIntervalNs = LONG_MAX;
virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
@@ -69,7 +69,7 @@
// cached data will be returned.
std::vector<std::shared_ptr<LogEvent>> mCachedData;
- long mLastPullTimeSec;
+ int64_t mLastPullTimeNs;
int clearCache();
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 2717d5c..83d59c0 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -26,10 +26,9 @@
public:
virtual ~StatsPullerManager() {}
- virtual void RegisterReceiver(int tagId,
- wp <PullDataReceiver> receiver,
- long intervalMs) {
- mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+ virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs) {
+ mPullerManager.RegisterReceiver(tagId, receiver, nextPullTimeNs, intervalNs);
};
virtual void UnRegisterReceiver(int tagId, wp <PullDataReceiver> receiver) {
@@ -45,13 +44,9 @@
mPullerManager.OnAlarmFired();
}
- virtual bool
- Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
- return mPullerManager.Pull(tagId, data);
- }
-
- void SetTimeBaseSec(const long timeBaseSec) {
- mPullerManager.SetTimeBaseSec(timeBaseSec);
+ virtual bool Pull(const int tagId, const int64_t timesNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ return mPullerManager.Pull(tagId, timesNs, data);
}
int ForceClearPullerCache() {
@@ -62,8 +57,8 @@
mPullerManager.SetStatsCompanionService(statsCompanionService);
}
- int ClearPullerCacheIfNecessary(long timestampSec) {
- return mPullerManager.ClearPullerCacheIfNecessary(timestampSec);
+ int ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ return mPullerManager.ClearPullerCacheIfNecessary(timestampNs);
}
private:
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index dd6406b..2f0e885 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -19,15 +19,17 @@
#include <android/os/IStatsCompanionService.h>
#include <cutils/log.h>
+#include <math.h>
#include <algorithm>
#include <climits>
+#include "../StatsService.h"
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
#include "ResourceHealthManagerPuller.h"
#include "ResourceThermalManagerPuller.h"
#include "StatsCompanionServicePuller.h"
-#include "StatsService.h"
+#include "StatsPullerManagerImpl.h"
#include "SubsystemSleepStatePuller.h"
#include "statslog.h"
@@ -47,89 +49,136 @@
const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
// wifi_bytes_transfer
{android::util::WIFI_BYTES_TRANSFER,
- {{2, 3, 4, 5}, {}, 1,
+ {{2, 3, 4, 5},
+ {},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
// wifi_bytes_transfer_by_fg_bg
{android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- {{3, 4, 5, 6}, {2}, 1,
+ {{3, 4, 5, 6},
+ {2},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
// mobile_bytes_transfer
{android::util::MOBILE_BYTES_TRANSFER,
- {{2, 3, 4, 5}, {}, 1,
+ {{2, 3, 4, 5},
+ {},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
// mobile_bytes_transfer_by_fg_bg
{android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- {{3, 4, 5, 6}, {2}, 1,
+ {{3, 4, 5, 6},
+ {2},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
// bluetooth_bytes_transfer
{android::util::BLUETOOTH_BYTES_TRANSFER,
- {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+ {{2, 3},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
// kernel_wakelock
{android::util::KERNEL_WAKELOCK,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
// subsystem_sleep_state
{android::util::SUBSYSTEM_SLEEP_STATE,
- {{}, {}, 1, new SubsystemSleepStatePuller()}},
+ {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
// cpu_time_per_freq
{android::util::CPU_TIME_PER_FREQ,
- {{3}, {2}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ {{3},
+ {2},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
// cpu_time_per_uid
{android::util::CPU_TIME_PER_UID,
- {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+ {{2, 3},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
// cpu_time_per_uid_freq
- // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+ // the throttling is 3sec, handled in
+ // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_TIME_PER_UID_FREQ,
- {{4}, {2,3}, 0, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+ {{4},
+ {2, 3},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
// cpu_active_time
- // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+ // the throttling is 3sec, handled in
+ // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_ACTIVE_TIME,
- {{2}, {}, 0, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+ {{2},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
// cpu_cluster_time
- // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+ // the throttling is 3sec, handled in
+ // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_CLUSTER_TIME,
- {{3}, {2}, 0, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+ {{3},
+ {2},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
// wifi_activity_energy_info
- {android::util::WIFI_ACTIVITY_ENERGY_INFO,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+ {android::util::WIFI_ACTIVITY_INFO,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
// modem_activity_info
{android::util::MODEM_ACTIVITY_INFO,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
// bluetooth_activity_info
{android::util::BLUETOOTH_ACTIVITY_INFO,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
// system_uptime
{android::util::SYSTEM_UPTIME,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
// disk_space
{android::util::DISK_SPACE,
- {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
// remaining_battery_capacity
{android::util::REMAINING_BATTERY_CAPACITY,
- {{}, {}, 1, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
// full_battery_capacity
{android::util::FULL_BATTERY_CAPACITY,
- {{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {{4,5,6,7,8},
- {2,3},
- 0,
+ {{4, 5, 6, 7, 8},
+ {2, 3},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// temperature
{android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
-StatsPullerManagerImpl::StatsPullerManagerImpl()
- : mCurrentPullingInterval(LONG_MAX) {
+StatsPullerManagerImpl::StatsPullerManagerImpl() : mNextPullTimeNs(LONG_MAX) {
}
-bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManagerImpl::Pull(const int tagId, const int64_t timeNs,
+ vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
- bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+ bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(timeNs, data);
VLOG("pulled %d items", (int)data->size());
return ret;
} else {
@@ -148,12 +197,14 @@
}
void StatsPullerManagerImpl::updateAlarmLocked() {
- long currentTimeMs = getElapsedRealtimeMillis();
- long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval -
- (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval;
+ if (mNextPullTimeNs == LONG_MAX) {
+ VLOG("No need to set alarms. Skipping");
+ return;
+ }
+
sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
if (statsCompanionServiceCopy != nullptr) {
- statsCompanionServiceCopy->setPullingAlarms(nextAlarmTimeMs, mCurrentPullingInterval);
+ statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
} else {
VLOG("StatsCompanionService not available. Alarm not set.");
}
@@ -174,7 +225,7 @@
}
void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
- long intervalMs) {
+ int64_t nextPullTimeNs, int64_t intervalNs) {
AutoMutex _l(mLock);
auto& receivers = mReceivers[tagId];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -185,21 +236,24 @@
}
ReceiverInfo receiverInfo;
receiverInfo.receiver = receiver;
- receiverInfo.timeInfo.first = intervalMs;
- receivers.push_back(receiverInfo);
// Round it to the nearest minutes. This is the limit of alarm manager.
- // In practice, we should limit it higher.
- long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60;
+ // In practice, we should always have larger buckets.
+ int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
// Scheduled pulling should be at least 1 min apart.
// This can be lower in cts tests, in which case we round it to 1 min.
- if (roundedIntervalMs < 60 * 1000) {
- roundedIntervalMs = 60 * 1000;
+ if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
+ roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
}
+
+ receiverInfo.intervalNs = roundedIntervalNs;
+ receiverInfo.nextPullTimeNs = nextPullTimeNs;
+ receivers.push_back(receiverInfo);
+
// There is only one alarm for all pulled events. So only set it to the smallest denom.
- if (roundedIntervalMs < mCurrentPullingInterval) {
- VLOG("Updating pulling interval %ld", intervalMs);
- mCurrentPullingInterval = roundedIntervalMs;
+ if (nextPullTimeNs < mNextPullTimeNs) {
+ VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
+ mNextPullTimeNs = nextPullTimeNs;
updateAlarmLocked();
}
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
@@ -224,16 +278,22 @@
void StatsPullerManagerImpl::OnAlarmFired() {
AutoMutex _l(mLock);
- uint64_t currentTimeMs = getElapsedRealtimeMillis();
+ int64_t currentTimeNs = getElapsedRealtimeNs();
+
+ int64_t minNextPullTimeNs = LONG_MAX;
vector<pair<int, vector<ReceiverInfo*>>> needToPull =
vector<pair<int, vector<ReceiverInfo*>>>();
for (auto& pair : mReceivers) {
vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
if (pair.second.size() != 0) {
- for (auto& receiverInfo : pair.second) {
- if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
+ for (ReceiverInfo& receiverInfo : pair.second) {
+ if (receiverInfo.nextPullTimeNs < currentTimeNs) {
receivers.push_back(&receiverInfo);
+ } else {
+ if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
+ minNextPullTimeNs = receiverInfo.nextPullTimeNs;
+ }
}
}
if (receivers.size() > 0) {
@@ -244,18 +304,29 @@
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- if (Pull(pullInfo.first, &data)) {
+ if (Pull(pullInfo.first, currentTimeNs, &data)) {
for (const auto& receiverInfo : pullInfo.second) {
sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
if (receiverPtr != nullptr) {
receiverPtr->onDataPulled(data);
- receiverInfo->timeInfo.second = currentTimeMs;
+ // we may have just come out of a coma, compute next pull time
+ receiverInfo->nextPullTimeNs =
+ ceil((double_t)(currentTimeNs - receiverInfo->nextPullTimeNs) /
+ receiverInfo->intervalNs) *
+ receiverInfo->intervalNs +
+ receiverInfo->nextPullTimeNs;
+ if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
+ minNextPullTimeNs = receiverInfo->nextPullTimeNs;
+ }
} else {
VLOG("receiver already gone.");
}
}
}
}
+
+ mNextPullTimeNs = minNextPullTimeNs;
+ updateAlarmLocked();
}
int StatsPullerManagerImpl::ForceClearPullerCache() {
@@ -266,10 +337,10 @@
return totalCleared;
}
-int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
+int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(int64_t timestampNs) {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
+ totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
}
return totalCleared;
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 682ad33..8c771f3 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -41,7 +41,7 @@
std::vector<int> nonAdditiveFields;
// How long should the puller wait before doing an actual pull again. Default
// 1 sec. Set this to 0 if this is handled elsewhere.
- long coolDownSec = 1;
+ int64_t coolDownNs = 1 * NS_PER_SEC;
// The actual puller
sp<StatsPuller> puller;
} PullAtomInfo;
@@ -50,7 +50,8 @@
public:
static StatsPullerManagerImpl& GetInstance();
- void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs);
+ void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs);
void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
@@ -59,13 +60,11 @@
void OnAlarmFired();
- bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
-
- void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+ bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
int ForceClearPullerCache();
- int ClearPullerCacheIfNecessary(long timestampSec);
+ int ClearPullerCacheIfNecessary(int64_t timestampNs);
void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
@@ -77,8 +76,8 @@
sp<IStatsCompanionService> mStatsCompanionService = nullptr;
typedef struct {
- // pull_interval_sec : last_pull_time_sec
- std::pair<uint64_t, uint64_t> timeInfo;
+ int64_t nextPullTimeNs;
+ int64_t intervalNs;
wp<PullDataReceiver> receiver;
} ReceiverInfo;
@@ -90,12 +89,7 @@
void updateAlarmLocked();
- long mCurrentPullingInterval;
-
- // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
- // bucket size. All pulled metrics start pulling based on this time, so that they can be
- // correctly attributed to the correct buckets.
- long mTimeBaseSec;
+ int64_t mNextPullTimeNs;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index f0e0df1..b13c3e7 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -112,7 +112,8 @@
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
+ mStatsPullerManager->RegisterReceiver(
+ mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -255,7 +256,7 @@
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mStatsPullerManager->Pull(mPullTagId, getElapsedRealtimeNs(), &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
return;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index e19e236..d0f510d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -110,10 +110,12 @@
}
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
- if (!metric.has_condition() && mPullTagId != -1) {
- VLOG("Setting up periodic pulling for %d", mPullTagId);
- mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
+ // Kicks off the puller immediately.
+ if (mPullTagId != -1) {
+ mStatsPullerManager->RegisterReceiver(
+ mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
+
VLOG("value metric %lld created. bucket size %lld start_time: %lld",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -194,26 +196,21 @@
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
-void ValueMetricProducer::onConditionChangedLocked(const bool condition, const uint64_t eventTime) {
+void ValueMetricProducer::onConditionChangedLocked(const bool condition,
+ const uint64_t eventTimeNs) {
mCondition = condition;
- if (eventTime < mCurrentBucketStartTimeNs) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTime,
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
- flushIfNeededLocked(eventTime);
+ flushIfNeededLocked(eventTimeNs);
if (mPullTagId != -1) {
- if (mCondition == true) {
- mStatsPullerManager->RegisterReceiver(mPullTagId, this, mBucketSizeNs / 1000 / 1000);
- } else if (mCondition == false) {
- mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
- }
-
vector<shared_ptr<LogEvent>> allData;
- if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
+ if (mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
if (allData.size() == 0) {
return;
}
@@ -315,8 +312,13 @@
if (mPullTagId != -1) { // for pulled events
if (mCondition == true) {
- interval.start = value;
- interval.startUpdated = true;
+ if (!interval.startUpdated) {
+ interval.start = value;
+ interval.startUpdated = true;
+ } else {
+ // skip it if there is already value recorded for the start
+ VLOG("Already recorded value for this dimension %s", eventKey.toString().c_str());
+ }
} else {
// Generally we expect value to be monotonically increasing.
// If not, there was a reset event. We take the absolute value as
@@ -385,6 +387,7 @@
int tainted = 0;
for (const auto& slice : mCurrentSlicedBucket) {
tainted += slice.second.tainted;
+ tainted += slice.second.startUpdated;
info.mValue = slice.second.sum;
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 796e83a..45d9531 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -53,7 +53,7 @@
if (mPullTagId != -1) {
vector<shared_ptr<LogEvent>> allData;
- mStatsPullerManager->Pull(mPullTagId, &allData);
+ mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
if (allData.size() == 0) {
// This shouldn't happen since this valuemetric is not useful now.
}
@@ -159,6 +159,10 @@
FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
+ FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3);
};
} // namespace statsd
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index cab61e9..efd810f 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -221,7 +221,8 @@
int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
- if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) {
+ if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
+ uid != AID_ROOT) {
bucketSizeMillis = 5 * 60 * 1000LL;
}
return bucketSizeMillis;
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index 920273b..3a0c224 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -20,5 +20,5 @@
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 73e7c44..5a6aba6 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -356,7 +356,7 @@
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 2e6a0f0..2b91324 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 @@
*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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 5c4eda8..04ce73a 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -210,7 +210,7 @@
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 @@
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/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 2583c95..7ca66fd 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -63,7 +63,7 @@
// For now we still need this so that it doesn't do real pulling.
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
@@ -213,10 +213,11 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
event->write("some value");
@@ -281,10 +282,11 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write("some value");
@@ -372,10 +374,11 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(1000);
@@ -420,7 +423,7 @@
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
GaugeMetric metric;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index a8eb2703..c650a06 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -45,6 +45,8 @@
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
+const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
+const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
/*
@@ -62,7 +64,7 @@
// For now we still need this so that it doesn't do real pulling.
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
@@ -141,11 +143,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(tagId);
@@ -154,7 +157,8 @@
data->push_back(event);
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
event->write(tagId);
@@ -260,10 +264,11 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(tagId);
@@ -428,6 +433,376 @@
std::ceil(1.0 * event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
}
+// Test value metric no condition, the pull on bucket boundary come in time and too late
+TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ tagId, bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ vector<shared_ptr<LogEvent>> allData;
+ // pull 1
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(tagId);
+ event->write(11);
+ event->init();
+ allData.push_back(event);
+
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ // startUpdated:true tainted:0 sum:0 start:11
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(11, curInterval.start);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+ // pull 2 at correct time
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+ event->write(tagId);
+ event->write(23);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // tartUpdated:false tainted:0 sum:12
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+ // pull 3 come late.
+ // The previous bucket gets closed with error. (Has start value 23, no ending)
+ // Another bucket gets closed with error. (No start, but ending with 36)
+ // The new bucket is back to normal.
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket6StartTimeNs + 1);
+ event->write(tagId);
+ event->write(36);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // startUpdated:false tainted:0 sum:12
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(36, curInterval.start);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(4UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].mValue);
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[2].mValue);
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[3].mValue);
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
+ * was delivered late.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // condition becomes false
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // startUpdated:false tainted:0 sum:0 start:100
+ EXPECT_EQ(100, curInterval.start);
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ // pull on bucket boundary come late, condition change happens before it
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(false, curInterval.startUpdated);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+
+ // Now the alarm is delivered.
+ // since the condition turned to off before this pull finish, it has no effect
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(false, curInterval.startUpdated);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
+ * change to false, and then true again. This is due to alarm delivered late.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // condition becomes false
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // condition becomes true again
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
+ event->write(tagId);
+ event->write(130);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // startUpdated:false tainted:0 sum:0 start:100
+ EXPECT_EQ(100, curInterval.start);
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ // pull on bucket boundary come late, condition change happens before it
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(false, curInterval.startUpdated);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+
+ // condition changed to true again, before the pull alarm is delivered
+ valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(130, curInterval.start);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+
+ // Now the alarm is delivered, but it is considered late, it has no effect
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(130, curInterval.start);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late because the puller is
+ * very slow.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ // condition becomes false
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 20);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // startUpdated:false tainted:0 sum:0 start:100
+ EXPECT_EQ(100, curInterval.start);
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ // pull on bucket boundary come late, condition change happens before it.
+ // But puller is very slow in this one, so the data come after bucket finish
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(false, curInterval.startUpdated);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+
+ // Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
+ // so this one comes even later
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 30);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(false, curInterval.startUpdated);
+ EXPECT_EQ(1, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index f040bf9..5afaba6 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -35,9 +35,11 @@
class MockStatsPullerManager : public StatsPullerManager {
public:
- MOCK_METHOD3(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver, long intervalMs));
+ MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
+ int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
- MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
+ MOCK_METHOD3(Pull, bool(const int pullCode, const int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data));
};
class MockUidMap : public UidMap {
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index ce44a35..6621500 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -152,42 +152,42 @@
}
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 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> 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> 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/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3af6105..015c4b6 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,4 +1,5 @@
Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+Landroid/accounts/IAccountAuthenticatorResponse$Stub;-><init>()V
Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManager;
Landroid/accounts/IAccountManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/animation/LayoutTransition;->cancel()V
@@ -6,6 +7,7 @@
Landroid/animation/ValueAnimator;->sDurationScale:F
Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/ActivityGroup;->mLocalActivityManager:Landroid/app/LocalActivityManager;
Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo;
Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z
Landroid/app/ActivityManager;->getMaxRecentTasksStatic()I
@@ -62,8 +64,10 @@
Landroid/app/ActivityThread$AppBindData;->appInfo:Landroid/content/pm/ApplicationInfo;
Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle;
+Landroid/app/ActivityThread$AppBindData;->persistent:Z
Landroid/app/ActivityThread$AppBindData;->processName:Ljava/lang/String;
Landroid/app/ActivityThread$AppBindData;->providers:Ljava/util/List;
+Landroid/app/ActivityThread$AppBindData;->restrictedBackupMode:Z
Landroid/app/ActivityThread$BindServiceData;->intent:Landroid/content/Intent;
Landroid/app/ActivityThread$BindServiceData;->token:Landroid/os/IBinder;
Landroid/app/ActivityThread$CreateServiceData;->compatInfo:Landroid/content/res/CompatibilityInfo;
@@ -88,11 +92,13 @@
Landroid/app/ActivityThread$H;->BIND_SERVICE:I
Landroid/app/ActivityThread$H;->CREATE_SERVICE:I
Landroid/app/ActivityThread$H;->DUMP_PROVIDER:I
+Landroid/app/ActivityThread$H;->ENTER_ANIMATION_COMPLETE:I
Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I
Landroid/app/ActivityThread$H;->GC_WHEN_IDLE:I
Landroid/app/ActivityThread$H;->INSTALL_PROVIDER:I
Landroid/app/ActivityThread$H;->RECEIVER:I
Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I
+Landroid/app/ActivityThread$H;->SCHEDULE_CRASH:I
Landroid/app/ActivityThread$H;->SERVICE_ARGS:I
Landroid/app/ActivityThread$H;->STOP_SERVICE:I
Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I
@@ -101,6 +107,7 @@
Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList;
Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData;
+Landroid/app/ActivityThread;->mConfiguration:Landroid/content/res/Configuration;
Landroid/app/ActivityThread;->mDensityCompatMode:Z
Landroid/app/ActivityThread;->mH:Landroid/app/ActivityThread$H;
Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application;
@@ -109,8 +116,10 @@
Landroid/app/ActivityThread;->mLocalProviders:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->mNumVisibleActivities:I
Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mPendingConfiguration:Landroid/content/res/Configuration;
Landroid/app/ActivityThread;->mProviderMap:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->mResourcePackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mResourcesManager:Landroid/app/ResourcesManager;
Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->performNewIntents(Landroid/os/IBinder;Ljava/util/List;Z)V
Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
@@ -145,6 +154,7 @@
Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams;
Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController;
Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application;
+Landroid/app/AppGlobals;->getInitialPackage()Ljava/lang/String;
Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager;
Landroid/app/Application;->attach(Landroid/content/Context;)V
Landroid/app/Application;->collectActivityLifecycleCallbacks()[Ljava/lang/Object;
@@ -165,21 +175,26 @@
Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/String;ILandroid/content/pm/IPackageStatsObserver;)V
Landroid/app/ApplicationPackageManager;-><init>(Landroid/app/ContextImpl;Landroid/content/pm/IPackageManager;)V
Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager;
+Landroid/app/ApplicationPackageManager;->shouldShowRequestPermissionRationale(Ljava/lang/String;)Z
Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
Landroid/app/AppOpsManager;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
Landroid/app/AppOpsManager;->mService:Lcom/android/internal/app/IAppOpsService;
Landroid/app/AppOpsManager;->noteOp(I)I
Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->OP_AUDIO_NOTIFICATION_VOLUME:I
Landroid/app/AppOpsManager;->OP_COARSE_LOCATION:I
Landroid/app/AppOpsManager$OpEntry;->getDuration()I
Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J
Landroid/app/AppOpsManager;->OP_FINE_LOCATION:I
Landroid/app/AppOpsManager;->OP_GET_USAGE_STATS:I
Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I
+Landroid/app/AppOpsManager;->OP_READ_CONTACTS:I
Landroid/app/AppOpsManager;->OP_READ_PHONE_STATE:I
Landroid/app/AppOpsManager;->OP_READ_SMS:I
+Landroid/app/AppOpsManager;->OP_VIBRATE:I
Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
+Landroid/app/AppOpsManager;->OP_WRITE_CONTACTS:I
Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I
Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
@@ -203,6 +218,7 @@
Landroid/app/ContextImpl;->getPreferencesDir()Ljava/io/File;
Landroid/app/ContextImpl;->getReceiverRestrictedContext()Landroid/content/Context;
Landroid/app/ContextImpl;->mBasePackageName:Ljava/lang/String;
+Landroid/app/ContextImpl;->mClassLoader:Ljava/lang/ClassLoader;
Landroid/app/ContextImpl;->mContentResolver:Landroid/app/ContextImpl$ApplicationContentResolver;
Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
Landroid/app/ContextImpl;->mOpPackageName:Ljava/lang/String;
@@ -212,6 +228,7 @@
Landroid/app/ContextImpl;->mResources:Landroid/content/res/Resources;
Landroid/app/ContextImpl;->mServiceCache:[Ljava/lang/Object;
Landroid/app/ContextImpl;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/app/ContextImpl;->mThemeResource:I
Landroid/app/ContextImpl;->scheduleFinalCleanup(Ljava/lang/String;Ljava/lang/String;)V
Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V
Landroid/app/ContextImpl;->sSharedPrefsCache:Landroid/util/ArrayMap;
@@ -229,26 +246,37 @@
Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
Landroid/app/Fragment;->mWho:Ljava/lang/String;
Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
+Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
+Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender;
Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
+Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String;
Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V
Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
+Landroid/app/IActivityManager;->requestBugReport(I)V
Landroid/app/IActivityManager;->resumeAppSwitches()V
Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
Landroid/app/IActivityManager;->setTaskResizeable(II)V
+Landroid/app/IActivityManager;->stopService(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;I)I
Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
+Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J
Landroid/app/IActivityManager$Stub$Proxy;->isAppForeground(I)Z
Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/app/IActivityManager;->unbindService(Landroid/app/IServiceConnection;)Z
+Landroid/app/IActivityManager;->unstableProviderDied(Landroid/os/IBinder;)V
Landroid/app/IAlarmManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I
Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
+Landroid/app/INotificationManager;->cancelAllNotifications(Ljava/lang/String;I)V
+Landroid/app/INotificationManager;->cancelNotificationWithTag(Ljava/lang/String;Ljava/lang/String;II)V
+Landroid/app/INotificationManager;->cancelToast(Ljava/lang/String;Landroid/app/ITransientNotification;)V
+Landroid/app/INotificationManager;->enqueueToast(Ljava/lang/String;Landroid/app/ITransientNotification;I)V
Landroid/app/INotificationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/INotificationManager;
Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/app/Instrumentation;->execStartActivities(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Landroid/app/Activity;[Landroid/content/Intent;Landroid/os/Bundle;)V
@@ -256,9 +284,12 @@
Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;)Landroid/app/Instrumentation$ActivityResult;
Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/Instrumentation$ActivityResult;
Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler;
+Landroid/app/IProcessObserver$Stub;-><init>()V
Landroid/app/ISearchManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/ISearchManager;
Landroid/app/ISearchManager$Stub$Proxy;->getGlobalSearchActivity()Landroid/content/ComponentName;
Landroid/app/ISearchManager$Stub$Proxy;->getWebSearchActivity()Landroid/content/ComponentName;
+Landroid/app/IServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IServiceConnection;
+Landroid/app/IStopUserCallback;->userStopped(I)V
Landroid/app/IUiModeManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
Landroid/app/job/IJobScheduler$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/job/IJobScheduler;
@@ -285,16 +316,21 @@
Landroid/app/LoadedApk;->rewriteRValues(Ljava/lang/ClassLoader;Ljava/lang/String;I)V
Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map;
Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList;
+Landroid/app/LocalActivityManager;->mParent:Landroid/app/Activity;
+Landroid/app/LocalActivityManager;->mResumed:Landroid/app/LocalActivityManager$LocalActivityRecord;
+Landroid/app/LocalActivityManager;->mSingleMode:Z
Landroid/app/NativeActivity;->hideIme(I)V
Landroid/app/NativeActivity;->setWindowFlags(II)V
Landroid/app/NativeActivity;->setWindowFormat(I)V
Landroid/app/NativeActivity;->showIme(I)V
Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList;
+Landroid/app/Notification$Builder;->makePublicContentView()Landroid/widget/RemoteViews;
Landroid/app/Notification$Builder;->setChannel(Ljava/lang/String;)Landroid/app/Notification$Builder;
Landroid/app/Notification;->isGroupSummary()Z
Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager;
Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V
Landroid/app/NotificationManager;->sService:Landroid/app/INotificationManager;
+Landroid/app/Notification;->mChannelId:Ljava/lang/String;
Landroid/app/Notification;->mGroupKey:Ljava/lang/String;
Landroid/app/Notification;->mLargeIcon:Landroid/graphics/drawable/Icon;
Landroid/app/Notification;->mSmallIcon:Landroid/graphics/drawable/Icon;
@@ -307,6 +343,7 @@
Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView;
Landroid/app/QueuedWork;->addFinisher(Ljava/lang/Runnable;)V
Landroid/app/QueuedWork;->removeFinisher(Ljava/lang/Runnable;)V
+Landroid/app/QueuedWork;->sFinishers:Ljava/util/LinkedList;
Landroid/app/ResourcesManager;->appendLibAssetForMainAssetPath(Ljava/lang/String;Ljava/lang/String;)V
Landroid/app/ResourcesManager;->getInstance()Landroid/app/ResourcesManager;
Landroid/app/ResourcesManager;->mActivityResourceReferences:Ljava/util/WeakHashMap;
@@ -320,7 +357,9 @@
Landroid/app/Service;->mThread:Landroid/app/ActivityThread;
Landroid/app/Service;->mToken:Landroid/os/IBinder;
Landroid/app/Service;->setForeground(Z)V
+Landroid/app/SharedPreferencesImpl;-><init>(Ljava/io/File;I)V
Landroid/app/SharedPreferencesImpl;->mFile:Ljava/io/File;
+Landroid/app/SharedPreferencesImpl;->startReloadIfChangedUnexpectedly()V
Landroid/app/StatusBarManager;->collapsePanels()V
Landroid/app/StatusBarManager;->disable(I)V
Landroid/app/StatusBarManager;->expandNotificationsPanel()V
@@ -427,6 +466,7 @@
Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
Landroid/content/IntentFilter;->mActions:Ljava/util/ArrayList;
+Landroid/content/Intent;->mExtras:Landroid/os/Bundle;
Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
Landroid/content/pm/ActivityInfo;->resizeMode:I
Landroid/content/pm/ApplicationInfo;->enabledSetting:I
@@ -437,18 +477,33 @@
Landroid/content/pm/ApplicationInfo;->privateFlags:I
Landroid/content/pm/ApplicationInfo;->scanPublicSourceDir:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String;
+Landroid/content/pm/ApplicationInfo;->secondaryCpuAbi:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->secondaryNativeLibraryDir:Ljava/lang/String;
Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName;
+Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
+Landroid/content/pm/IPackageManager;->addPermissionAsync(Landroid/content/pm/PermissionInfo;)Z
+Landroid/content/pm/IPackageManager;->addPermission(Landroid/content/pm/PermissionInfo;)Z
+Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I
+Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
+Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;
Landroid/content/pm/IPackageManager;->getInstallLocation()I
Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
+Landroid/content/pm/IPackageManager;->getProviderInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ProviderInfo;
+Landroid/content/pm/IPackageManager;->getReceiverInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/IPackageManager;->getServiceInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ServiceInfo;
Landroid/content/pm/IPackageManager;->setApplicationEnabledSetting(Ljava/lang/String;IIILjava/lang/String;)V
Landroid/content/pm/IPackageManager;->setComponentEnabledSetting(Landroid/content/ComponentName;III)V
+Landroid/content/pm/IPackageManager;->setInstallerPackageName(Ljava/lang/String;Ljava/lang/String;)V
Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
Landroid/content/pm/IPackageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageManager;
+Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
+Landroid/content/pm/IPackageMoveObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageMoveObserver;
+Landroid/content/pm/IPackageMoveObserver$Stub;-><init>()V
Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
+Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
@@ -473,14 +528,29 @@
Landroid/content/pm/PackageParser$Component;->className:Ljava/lang/String;
Landroid/content/pm/PackageParser$Component;->getComponentName()Landroid/content/ComponentName;
Landroid/content/pm/PackageParser$Component;->intents:Ljava/util/ArrayList;
+Landroid/content/pm/PackageParser;->generateActivityInfo(Landroid/content/pm/PackageParser$Activity;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ActivityInfo;
Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo;
Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->generateProviderInfo(Landroid/content/pm/PackageParser$Provider;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ProviderInfo;
+Landroid/content/pm/PackageParser;->generateServiceInfo(Landroid/content/pm/PackageParser$Service;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ServiceInfo;
Landroid/content/pm/PackageParser;-><init>()V
+Landroid/content/pm/PackageParser$Instrumentation;->info:Landroid/content/pm/InstrumentationInfo;
+Landroid/content/pm/PackageParser$IntentInfo;->banner:I
+Landroid/content/pm/PackageParser$IntentInfo;->hasDefault:Z
+Landroid/content/pm/PackageParser$IntentInfo;->icon:I
+Landroid/content/pm/PackageParser$IntentInfo;-><init>()V
+Landroid/content/pm/PackageParser$IntentInfo;->labelRes:I
+Landroid/content/pm/PackageParser$IntentInfo;->logo:I
+Landroid/content/pm/PackageParser$IntentInfo;->nonLocalizedLabel:Ljava/lang/CharSequence;
Landroid/content/pm/PackageParser$Package;->activities:Ljava/util/ArrayList;
Landroid/content/pm/PackageParser$Package;->applicationInfo:Landroid/content/pm/ApplicationInfo;
+Landroid/content/pm/PackageParser$Package;->instrumentation:Ljava/util/ArrayList;
+Landroid/content/pm/PackageParser$Package;->mAppMetaData:Landroid/os/Bundle;
Landroid/content/pm/PackageParser$Package;->mVersionCode:I
Landroid/content/pm/PackageParser$Package;->mVersionName:Ljava/lang/String;
Landroid/content/pm/PackageParser$Package;->packageName:Ljava/lang/String;
+Landroid/content/pm/PackageParser$Package;->permissionGroups:Ljava/util/ArrayList;
+Landroid/content/pm/PackageParser$Package;->permissions:Ljava/util/ArrayList;
Landroid/content/pm/PackageParser$Package;->providers:Ljava/util/ArrayList;
Landroid/content/pm/PackageParser$Package;->receivers:Ljava/util/ArrayList;
Landroid/content/pm/PackageParser$Package;->requestedPermissions:Ljava/util/ArrayList;
@@ -491,6 +561,7 @@
Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
Landroid/content/pm/PackageParser$Provider;->info:Landroid/content/pm/ProviderInfo;
Landroid/content/pm/PackageParser$ProviderIntentInfo;->provider:Landroid/content/pm/PackageParser$Provider;
+Landroid/content/pm/PackageParser$Service;->info:Landroid/content/pm/ServiceInfo;
Landroid/content/pm/PackageParser$ServiceIntentInfo;->service:Landroid/content/pm/PackageParser$Service;
Landroid/content/pm/PackageUserState;-><init>()V
Landroid/content/pm/ParceledListSlice;-><init>(Ljava/util/List;)V
@@ -529,14 +600,18 @@
Landroid/content/res/CompatibilityInfo;->applicationScale:F
Landroid/content/res/CompatibilityInfo;->DEFAULT_COMPATIBILITY_INFO:Landroid/content/res/CompatibilityInfo;
Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
+Landroid/content/res/DrawableCache;-><init>()V
Landroid/content/res/ObbInfo;->salt:[B
Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
+Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager;
Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
+Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager;
Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache;
Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration;
Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache;
Landroid/content/res/ResourcesImpl;->mPreloading:Z
+Landroid/content/res/ResourcesImpl;->mStateListAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray;
Landroid/content/res/ResourcesImpl;->sPreloadedComplexColors:Landroid/util/LongSparseArray;
Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray;
@@ -552,6 +627,7 @@
Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V
Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V
Landroid/content/res/StringBlock;-><init>(JZ)V
+Landroid/content/res/ThemedResourceCache;->onConfigurationChange(I)V
Landroid/content/res/TypedArray;->extractThemeAttrs()[I
Landroid/content/res/TypedArray;->getNonConfigurationString(II)Ljava/lang/String;
Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z
@@ -570,7 +646,10 @@
Landroid/content/res/XmlBlock$Parser;->mBlock:Landroid/content/res/XmlBlock;
Landroid/content/res/XmlBlock$Parser;->mParseState:J
Landroid/content/SearchRecentSuggestionsProvider;->mSuggestionProjection:[Ljava/lang/String;
+Landroid/content/SyncContext;->setStatusText(Ljava/lang/String;)V
Landroid/content/SyncStatusInfo;->lastSuccessTime:J
+Landroid/content/UriMatcher;->mChildren:Ljava/util/ArrayList;
+Landroid/content/UriMatcher;->mText:Ljava/lang/String;
Landroid/database/AbstractCursor;->mExtras:Landroid/os/Bundle;
Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
Landroid/database/AbstractCursor;->mRowIdColumnIndex:I
@@ -589,6 +668,7 @@
Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I
Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String;
Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
+Landroid/graphics/AvoidXfermode$Mode;->AVOID:Landroid/graphics/AvoidXfermode$Mode;
Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
Landroid/graphics/Bitmap$Config;->nativeInt:I
@@ -609,6 +689,11 @@
Landroid/graphics/Camera;->native_instance:J
Landroid/graphics/Canvas;-><init>(J)V
Landroid/graphics/Canvas;->release()V
+Landroid/graphics/Canvas;->save(I)I
+Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
+Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
+Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
+Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray;
@@ -621,6 +706,7 @@
Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
Landroid/graphics/drawable/BitmapDrawable;->mTargetDensity:I
Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
+Landroid/graphics/drawable/ColorDrawable$ColorState;->mUseColor:I
Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets;
Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
@@ -764,6 +850,7 @@
Landroid/hardware/Camera;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
+Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/HardwareBuffer;-><init>(J)V
Landroid/hardware/HardwareBuffer;->mNativeObject:J
Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
@@ -842,6 +929,9 @@
Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
+Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
+Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
+Landroid/inputmethodservice/InputMethodService$SettingsObserver;->shouldShowImeWithHardKeyboard()Z
Landroid/location/CountryDetector;->detectCountry()Landroid/location/Country;
Landroid/location/Country;->getCountryIso()Ljava/lang/String;
Landroid/location/Country;->getSource()I
@@ -938,11 +1028,15 @@
Landroid/media/AudioTrack;->mStreamType:I
Landroid/media/AudioTrack;->native_release()V
Landroid/media/AudioTrack;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
+Landroid/media/ExifInterface;->getDateTime()J
Landroid/media/IAudioService;->getStreamMaxVolume(I)I
Landroid/media/IAudioService;->getStreamVolume(I)I
Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V
Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/media/IMediaScannerService;->scanFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/media/IMediaScannerService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaScannerService;
+Landroid/media/IRemoteDisplayCallback;->onStateChanged(Landroid/media/RemoteDisplayState;)V
Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
@@ -995,6 +1089,8 @@
Landroid/media/RemoteDisplay;->notifyDisplayConnected(Landroid/view/Surface;IIII)V
Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V
Landroid/media/RemoteDisplay;->notifyDisplayError(I)V
+Landroid/media/RemoteDisplayState;->displays:Ljava/util/ArrayList;
+Landroid/media/RemoteDisplayState;-><init>()V
Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
Landroid/media/session/MediaSession;->mCallback:Landroid/media/session/MediaSession$CallbackMessageHandler;
@@ -1006,6 +1102,11 @@
Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I
Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/SubtitleTrack$RenderingWidget;->draw(Landroid/graphics/Canvas;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->onAttachedToWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->onDetachedFromWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->setOnChangedListener(Landroid/media/SubtitleTrack$RenderingWidget$OnChangedListener;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->setSize(II)V
Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
Landroid/media/ToneGenerator;->mNativeContext:J
Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -1045,11 +1146,16 @@
Landroid/net/ConnectivityManager;->TYPE_PROXY:I
Landroid/net/ConnectivityManager;->TYPE_WIFI_P2P:I
Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworks()[Landroid/net/Network;
Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/IpConfiguration;->httpProxy:Landroid/net/ProxyInfo;
Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
Landroid/net/LocalSocketImpl;->inboundFileDescriptors:[Ljava/io/FileDescriptor;
@@ -1073,6 +1179,7 @@
Landroid/net/NetworkStats;->txPackets:[J
Landroid/net/NetworkStats;->uid:[I
Landroid/net/NetworkTemplate;->buildTemplateWifi()Landroid/net/NetworkTemplate;
+Landroid/net/Proxy;->getProxy(Landroid/content/Context;Ljava/lang/String;)Ljava/net/Proxy;
Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;ILjava/lang/String;)V
Landroid/net/SntpClient;-><init>()V
Landroid/net/SSLCertificateSocketFactory;->castToOpenSSLSocket(Ljava/net/Socket;)Lcom/android/org/conscrypt/OpenSSLSocketImpl;
@@ -1099,6 +1206,7 @@
Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String;
Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V
Landroid/net/SSLSessionCache;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache;
+Landroid/net/TrafficStats;->getMobileIfaces()[Ljava/lang/String;
Landroid/net/TrafficStats;->getRxBytes(Ljava/lang/String;)J
Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService;
Landroid/net/TrafficStats;->getTxBytes(Ljava/lang/String;)J
@@ -1107,6 +1215,8 @@
Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I
Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
@@ -1140,10 +1250,12 @@
Landroid/net/wifi/WifiConfiguration;->apBand:I
Landroid/net/wifi/WifiConfiguration;->apChannel:I
Landroid/net/wifi/WifiConfiguration;->defaultGwMacAddress:Ljava/lang/String;
+Landroid/net/wifi/WifiConfiguration;->lastConnectUid:I
Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
Landroid/net/wifi/WifiEnterpriseConfig;->getCaCertificateAlias()Ljava/lang/String;
Landroid/net/wifi/WifiEnterpriseConfig;->getClientCertificateAlias()Ljava/lang/String;
+Landroid/net/wifi/WifiInfo;->DEFAULT_MAC_ADDRESS:Ljava/lang/String;
Landroid/net/wifi/WifiInfo;->getMeteredHint()Z
Landroid/net/wifi/WifiInfo;->mMacAddress:Ljava/lang/String;
Landroid/net/wifi/WifiInfo;->removeDoubleQuotes(Ljava/lang/String;)Ljava/lang/String;
@@ -1165,17 +1277,30 @@
Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable;
Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor;
Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V
+Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
Landroid/os/BatteryStats;->getUidStats()Landroid/util/SparseArray;
Landroid/os/BatteryStats$HistoryItem;->states2:I
Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I
Landroid/os/BatteryStats;->startIteratingHistoryLocked()Z
Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
+Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
+Landroid/os/BatteryStats$Uid;->getPackageStats()Landroid/util/ArrayMap;
Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap;
Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray;
Landroid/os/BatteryStats$Uid;->getUid()I
+Landroid/os/BatteryStats$Uid;->getVideoTurnedOnTimer()Landroid/os/BatteryStats$Timer;
Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
+Landroid/os/BatteryStats$Uid$Pkg;->getServiceStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid$Pkg;->getWakeupAlarmStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
+Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStartTime(JI)J
+Landroid/os/BatteryStats$Uid$Proc;->countExcessivePowers()I
+Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->overTime:J
+Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->type:I
+Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->usedTime:J
+Landroid/os/BatteryStats$Uid$Proc;->getExcessivePower(I)Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;
Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J
Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J
Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
@@ -1222,6 +1347,7 @@
Landroid/os/Debug$MemoryInfo;->otherSwappedOutPss:I
Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->getVendorDirectory()Ljava/io/File;
+Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
Landroid/os/FileObserver$ObserverThread;->onEvent(IILjava/lang/String;)V
Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J
Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z
@@ -1233,6 +1359,7 @@
Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I
Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z
Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z
Landroid/os/Handler;-><init>(Z)V
@@ -1247,6 +1374,7 @@
Landroid/os/IPowerManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/IPowerManager$Stub$Proxy;->isLightDeviceIdleMode()Z
Landroid/os/IPowerManager;->userActivity(JII)V
+Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/IUserManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue;
@@ -1290,6 +1418,7 @@
Landroid/os/Process;->isIsolated(I)Z
Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
+Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
Landroid/os/SELinux;->isSELinuxEnabled()Z
Landroid/os/SELinux;->isSELinuxEnforced()Z
Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
@@ -1451,13 +1580,16 @@
Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
+Landroid/provider/Browser$BookmarkColumns;->DATE:Ljava/lang/String;
Landroid/provider/Browser;->BOOKMARKS_URI:Landroid/net/Uri;
Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z
Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V
Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V
Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V
Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
+Landroid/provider/Browser;->SEARCHES_URI:Landroid/net/Uri;
Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/provider/Browser;->updateVisitedHistory(Landroid/content/ContentResolver;Ljava/lang/String;Z)V
Landroid/provider/CalendarContract$CalendarAlerts;->findNextAlarmTime(Landroid/content/ContentResolver;J)J
Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V
Landroid/provider/Settings$ContentProviderHolder;->mContentProvider:Landroid/content/IContentProvider;
@@ -1685,6 +1817,7 @@
Landroid/R$styleable;->Window:[I
Landroid/R$styleable;->Window_windowBackground:I
Landroid/R$styleable;->Window_windowFrame:I
+Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J
Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
Landroid/security/keystore/KeychainProtectionParams;->clearSecret()V
Landroid/security/keystore/KeychainProtectionParams;->getKeyDerivationParams()Landroid/security/keystore/KeyDerivationParams;
@@ -1833,6 +1966,7 @@
Landroid/telephony/SubscriptionManager;->getActiveSubscriptionIdList()[I
Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoCount()I
Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoList()Ljava/util/List;
+Landroid/telephony/SubscriptionManager;->getDefaultDataPhoneId()I
Landroid/telephony/SubscriptionManager;->getDefaultDataSubscriptionInfo()Landroid/telephony/SubscriptionInfo;
Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I
Landroid/telephony/SubscriptionManager;->getDefaultVoiceSubscriptionInfo()Landroid/telephony/SubscriptionInfo;
@@ -1872,6 +2006,7 @@
Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
Landroid/telephony/TelephonyManager;->isNetworkRoaming(I)Z
Landroid/telephony/TelephonyManager;->isVolteAvailable()Z
+Landroid/telephony/TelephonyManager;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
Landroid/text/AndroidBidi;->bidi(I[C[B)I
Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V
Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
@@ -1928,6 +2063,7 @@
Landroid/text/StaticLayout;->mColumns:I
Landroid/text/StaticLayout;->mLineCount:I
Landroid/text/StaticLayout;->mLines:[I
+Landroid/text/StaticLayout;->mMaximumVisibleLineCount:I
Landroid/text/TextLine;->mCharacterStyleSpanSet:Landroid/text/SpanSet;
Landroid/text/TextLine;->mMetricAffectingSpanSpanSet:Landroid/text/SpanSet;
Landroid/text/TextLine;->mReplacementSpanSpanSet:Landroid/text/SpanSet;
@@ -1959,13 +2095,17 @@
Landroid/util/Pools$SynchronizedPool;-><init>(I)V
Landroid/util/Rational;->mDenominator:I
Landroid/util/Rational;->mNumerator:I
+Landroid/util/Singleton;->get()Ljava/lang/Object;
+Landroid/util/Singleton;-><init>()V
Landroid/util/Singleton;->mInstance:Ljava/lang/Object;
Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/util/Slog;->w(Ljava/lang/String;Ljava/lang/String;)I
Landroid/util/SparseIntArray;->mKeys:[I
Landroid/util/SparseIntArray;->mSize:I
Landroid/util/SparseIntArray;->mValues:[I
Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager;
Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z
+Landroid/view/accessibility/AccessibilityManager;->mAccessibilityStateChangeListeners:Landroid/util/ArrayMap;
Landroid/view/accessibility/AccessibilityManager;->mIsEnabled:Z
Landroid/view/accessibility/AccessibilityManager;->mIsHighTextContrastEnabled:Z
Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager;
@@ -2015,6 +2155,7 @@
Landroid/view/inputmethod/InputMethodInfo;->mSubtypes:Landroid/view/inputmethod/InputMethodSubtypeArray;
Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
+Landroid/view/inputmethod/InputMethodManager;->focusOut(Landroid/view/View;)V
Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
@@ -2023,6 +2164,7 @@
Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
Landroid/view/inputmethod/InputMethodManager;->mService:Lcom/android/internal/view/IInputMethodManager;
Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
+Landroid/view/inputmethod/InputMethodManager;->peekInstance()Landroid/view/inputmethod/InputMethodManager;
Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V
Landroid/view/inputmethod/InputMethodManager;->sInstance:Landroid/view/inputmethod/InputMethodManager;
Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
@@ -2036,6 +2178,7 @@
Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
+Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
Landroid/view/IWindowManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -2066,6 +2209,7 @@
Landroid/view/LayoutInflater;->mPrivateFactory:Landroid/view/LayoutInflater$Factory2;
Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap;
Landroid/view/LayoutInflater;->setPrivateFactory(Landroid/view/LayoutInflater$Factory2;)V
+Landroid/view/MotionEvent;->getPointerIdBits()I
Landroid/view/MotionEvent;->HISTORY_CURRENT:I
Landroid/view/MotionEvent;->mNativePtr:J
Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F
@@ -2073,6 +2217,7 @@
Landroid/view/MotionEvent$PointerCoords;->mPackedAxisBits:J
Landroid/view/MotionEvent$PointerCoords;->mPackedAxisValues:[F
Landroid/view/MotionEvent;->scale(F)V
+Landroid/view/MotionEvent;->split(I)Landroid/view/MotionEvent;
Landroid/view/PointerIcon;->load(Landroid/content/Context;)Landroid/view/PointerIcon;
Landroid/view/PointerIcon;->mBitmapFrames:[Landroid/graphics/Bitmap;
Landroid/view/PointerIcon;->mBitmap:Landroid/graphics/Bitmap;
@@ -2093,6 +2238,8 @@
Landroid/view/RemoteAnimationTarget;->taskId:I
Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration;
Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V
+Landroid/view/RenderNode;->discardDisplayList()V
+Landroid/view/RenderNode;->output()V
Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
Landroid/view/ScaleGestureDetector;->mMinSpan:I
Landroid/view/SurfaceControl$PhysicalDisplayInfo;->appVsyncOffsetNanos:J
@@ -2127,6 +2274,7 @@
Landroid/view/textclassifier/TextClassificationManager;->getTextClassifier(I)Landroid/view/textclassifier/TextClassifier;
Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
Landroid/view/TextureView;->destroyHardwareLayer()V
+Landroid/view/TextureView;->destroyHardwareResources()V
Landroid/view/TextureView;->mLayer:Landroid/view/TextureLayer;
Landroid/view/TextureView;->mNativeWindow:J
Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
@@ -2138,6 +2286,7 @@
Landroid/view/VelocityTracker$Estimator;->yCoeff:[F
Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker;
Landroid/view/View;->applyDrawableToTransparentRegion(Landroid/graphics/drawable/Drawable;Landroid/graphics/Region;)V
+Landroid/view/View$AttachInfo;->mContentInsets:Landroid/graphics/Rect;
Landroid/view/View$AttachInfo;->mDrawingTime:J
Landroid/view/View$AttachInfo;->mStableInsets:Landroid/graphics/Rect;
Landroid/view/View;->clearAccessibilityFocus()V
@@ -2156,12 +2305,14 @@
Landroid/view/View;->fitsSystemWindows()Z
Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate;
Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V
+Landroid/view/View;->getHorizontalScrollFactor()F
Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix;
Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
Landroid/view/View;->getLocationOnScreen()[I
Landroid/view/View;->getRawTextAlignment()I
Landroid/view/View;->getRawTextDirection()I
Landroid/view/View;->getTransitionAlpha()F
+Landroid/view/View;->getVerticalScrollFactor()F
Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
Landroid/view/View;->getWindowDisplayFrame(Landroid/graphics/Rect;)V
Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V
@@ -2176,18 +2327,23 @@
Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
Landroid/view/ViewGroup;->mGroupFlags:I
Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/ViewGroup;->mPersistentDrawingCache:I
+Landroid/view/ViewGroup;->offsetChildrenTopAndBottom(I)V
Landroid/view/ViewGroup;->resetResolvedDrawables()V
Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V
Landroid/view/ViewGroup;->resetResolvedPadding()V
Landroid/view/ViewGroup;->resetResolvedTextAlignment()V
Landroid/view/ViewGroup;->resetResolvedTextDirection()V
Landroid/view/ViewGroup;->suppressLayout(Z)V
+Landroid/view/View;->includeForAccessibility()Z
Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
Landroid/view/View;->internalSetPadding(IIII)V
Landroid/view/View;->isPaddingResolved()Z
Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z
Landroid/view/View;->isVisibleToUser()Z
+Landroid/view/View$ListenerInfo;-><init>()V
Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener;
+Landroid/view/View$ListenerInfo;->mOnFocusChangeListener:Landroid/view/View$OnFocusChangeListener;
Landroid/view/View$ListenerInfo;->mOnLongClickListener:Landroid/view/View$OnLongClickListener;
Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
@@ -2200,10 +2356,13 @@
Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo;
Landroid/view/View;->mMinHeight:I
Landroid/view/View;->mMinWidth:I
+Landroid/view/View;->mPaddingBottom:I
Landroid/view/View;->mPaddingLeft:I
Landroid/view/View;->mPaddingRight:I
+Landroid/view/View;->mPaddingTop:I
Landroid/view/View;->mParent:Landroid/view/ViewParent;
Landroid/view/View;->mPrivateFlags3:I
+Landroid/view/View;->mPrivateFlags:I
Landroid/view/View;->mRecreateDisplayList:Z
Landroid/view/View;->mResources:Landroid/content/res/Resources;
Landroid/view/View;->mRight:I
@@ -2216,6 +2375,8 @@
Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap;
Landroid/view/View;->mViewFlags:I
Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V
+Landroid/view/View;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V
+Landroid/view/View;->performAccessibilityActionInternal(ILandroid/os/Bundle;)Z
Landroid/view/View;->recomputePadding()V
Landroid/view/View;->requestAccessibilityFocus()Z
Landroid/view/View;->resetDisplayList()V
@@ -2276,10 +2437,14 @@
Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J
Landroid/view/Window;->mAppName:Ljava/lang/String;
Landroid/view/Window;->mAppToken:Landroid/os/IBinder;
+Landroid/view/Window;->mCallback:Landroid/view/Window$Callback;
+Landroid/view/Window;->mContext:Landroid/content/Context;
Landroid/view/Window;->mHardwareAccelerated:Z
+Landroid/view/Window;->mWindowStyle:Landroid/content/res/TypedArray;
Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/webkit/WebResourceResponse;->mImmutable:Z
Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
+Landroid/webkit/WebViewClient;->onUnhandledInputEvent(Landroid/webkit/WebView;Landroid/view/InputEvent;)V
Landroid/webkit/WebView;->debugDump()V
Landroid/webkit/WebView;->disablePlatformNotifications()V
Landroid/webkit/WebView;->emulateShiftHeld()V
@@ -2288,6 +2453,7 @@
Landroid/webkit/WebViewFactory;->getWebViewContextAndSetProvider()Landroid/content/Context;
Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo;
Landroid/webkit/WebViewFactory;->sProviderInstance:Landroid/webkit/WebViewFactoryProvider;
+Landroid/webkit/WebView;->getTouchIconUrl()Ljava/lang/String;
Landroid/webkit/WebView;->getVisibleTitleHeight()I
Landroid/webkit/WebView;->isPaused()Z
Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
@@ -2309,6 +2475,8 @@
Landroid/widget/AbsListView;->mMaximumVelocity:I
Landroid/widget/AbsListView;->mMotionPosition:I
Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener;
+Landroid/widget/AbsListView;->mPendingCheckForLongPress:Landroid/widget/AbsListView$CheckForLongPress;
+Landroid/widget/AbsListView;->mPendingCheckForTap:Landroid/widget/AbsListView$CheckForTap;
Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin;
Landroid/widget/AbsListView;->mSelectionTopPadding:I
Landroid/widget/AbsListView;->mSelectorPosition:I
@@ -2321,6 +2489,8 @@
Landroid/widget/AbsListView$RecycleBin;->clear()V
Landroid/widget/AbsListView$RecycleBin;->mRecyclerListener:Landroid/widget/AbsListView$RecyclerListener;
Landroid/widget/AbsListView;->reportScrollStateChange(I)V
+Landroid/widget/AbsListView$SavedState;->firstId:J
+Landroid/widget/AbsListView$SavedState;->viewTop:I
Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V
Landroid/widget/AbsListView;->trackMotionScroll(II)Z
Landroid/widget/AbsSeekBar;->mIsDragging:Z
@@ -2339,7 +2509,9 @@
Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V
Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
+Landroid/widget/AutoCompleteTextView;->setForceIgnoreOutsideTouch(Z)V
Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/CompoundButton;->mOnCheckedChangeListener:Landroid/widget/CompoundButton$OnCheckedChangeListener;
Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver;
Landroid/widget/CursorAdapter;->mDataSetObserver:Landroid/database/DataSetObserver;
Landroid/widget/CursorAdapter;->mDataValid:Z
@@ -2350,6 +2522,7 @@
Landroid/widget/Editor;->mShowCursor:J
Landroid/widget/Editor;->mShowSoftInputOnFocus:Z
Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/ExpandableListView;->mGroupIndicator:Landroid/graphics/drawable/Drawable;
Landroid/widget/FastScroller;->mContainerRect:Landroid/graphics/Rect;
Landroid/widget/FastScroller;->mHeaderCount:I
Landroid/widget/FastScroller;->mLongList:Z
@@ -2456,13 +2629,17 @@
Landroid/widget/RemoteViews;->mergeRemoteViews(Landroid/widget/RemoteViews;)V
Landroid/widget/RemoteViews;->mPortrait:Landroid/widget/RemoteViews;
Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String;
+Landroid/widget/RemoteViews$ReflectionAction;->value:Ljava/lang/Object;
+Landroid/widget/RemoteViews$SetOnClickPendingIntent;->pendingIntent:Landroid/app/PendingIntent;
Landroid/widget/ScrollBarDrawable;->mVerticalThumb:Landroid/graphics/drawable/Drawable;
Landroid/widget/ScrollBarDrawable;->setHorizontalThumbDrawable(Landroid/graphics/drawable/Drawable;)V
Landroid/widget/ScrollBarDrawable;->setVerticalThumbDrawable(Landroid/graphics/drawable/Drawable;)V
+Landroid/widget/Scroller;->mInterpolator:Landroid/view/animation/Interpolator;
Landroid/widget/ScrollView;->mChildToScrollTo:Landroid/view/View;
Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
Landroid/widget/ScrollView;->mIsBeingDragged:Z
+Landroid/widget/ScrollView;->mMinimumVelocity:I
Landroid/widget/ScrollView;->mOverflingDistance:I
Landroid/widget/ScrollView;->mOverscrollDistance:I
Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller;
@@ -2476,7 +2653,11 @@
Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/TabHost$IntentContentStrategy;->getContentView()Landroid/view/View;
+Landroid/widget/TabHost$IntentContentStrategy;->tabClosed()V
+Landroid/widget/TabHost;->mTabSpecs:Ljava/util/List;
Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$ContentStrategy;
+Landroid/widget/TabWidget;->mSelectedTab:I
Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
Landroid/widget/TextView;->assumeLayout()V
Landroid/widget/TextView;->createEditorIfNeeded()V
@@ -2494,7 +2675,11 @@
Landroid/widget/TextView;->mTextPaint:Landroid/text/TextPaint;
Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V
Landroid/widget/Toast;->getService()Landroid/app/INotificationManager;
+Landroid/widget/Toast;->getWindowParams()Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/Toast;->mTN:Landroid/widget/Toast$TN;
Landroid/widget/Toast;->sService:Landroid/app/INotificationManager;
+Landroid/widget/Toast$TN;->mNextView:Landroid/view/View;
+Landroid/widget/Toast$TN;->mParams:Landroid/view/WindowManager$LayoutParams;
Landroid/widget/VideoView2;->getMediaController()Landroid/media/session/MediaController;
Landroid/widget/VideoView2$OnViewTypeChangedListener;->onViewTypeChanged(Landroid/view/View;I)V
Landroid/widget/VideoView2;->setOnViewTypeChangedListener(Landroid/widget/VideoView2$OnViewTypeChangedListener;)V
@@ -2577,6 +2762,7 @@
Lcom/android/internal/app/IAppOpsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IAppOpsService;
Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lang/String;)I
Lcom/android/internal/app/IAppOpsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->setMode(IILjava/lang/String;I)V
Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats;
Lcom/android/internal/app/IBatteryStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -2604,10 +2790,12 @@
Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/os/BatteryStatsImpl;->getBatteryRealtime(J)J
Lcom/android/internal/os/BatteryStatsImpl;->getDischargeAmount(I)I
Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J
Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J
Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl;->getUidStatsLocked(I)Lcom/android/internal/os/BatteryStatsImpl$Uid;
Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I
Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J
Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap;
@@ -2675,6 +2863,18 @@
Lcom/android/internal/R$string;->megabyteShort:I
Lcom/android/internal/R$string;->petabyteShort:I
Lcom/android/internal/R$string;->terabyteShort:I
+Lcom/android/internal/R$styleable;->AbsListView_cacheColorHint:I
+Lcom/android/internal/R$styleable;->AbsListView_choiceMode:I
+Lcom/android/internal/R$styleable;->AbsListView_drawSelectorOnTop:I
+Lcom/android/internal/R$styleable;->AbsListView_fastScrollAlwaysVisible:I
+Lcom/android/internal/R$styleable;->AbsListView_fastScrollEnabled:I
+Lcom/android/internal/R$styleable;->AbsListView:[I
+Lcom/android/internal/R$styleable;->AbsListView_listSelector:I
+Lcom/android/internal/R$styleable;->AbsListView_scrollingCache:I
+Lcom/android/internal/R$styleable;->AbsListView_smoothScrollbar:I
+Lcom/android/internal/R$styleable;->AbsListView_stackFromBottom:I
+Lcom/android/internal/R$styleable;->AbsListView_textFilterEnabled:I
+Lcom/android/internal/R$styleable;->AbsListView_transcriptMode:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_accountPreferences:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_accountType:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_customTokens:I
@@ -2682,6 +2882,28 @@
Lcom/android/internal/R$styleable;->AccountAuthenticator_icon:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_label:I
Lcom/android/internal/R$styleable;->AccountAuthenticator_smallIcon:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_allowTaskReparenting:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_configChanges:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_description:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_enabled:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_excludeFromRecents:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_exported:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_hardwareAccelerated:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity:[I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_icon:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_immersive:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_label:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_launchMode:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_logo:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_name:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_noHistory:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_permission:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_process:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_screenOrientation:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_taskAffinity:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_theme:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_uiOptions:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_windowSoftInputMode:I
Lcom/android/internal/R$styleable;->AndroidManifestApplication_enabled:I
Lcom/android/internal/R$styleable;->AndroidManifestApplication_hardwareAccelerated:I
Lcom/android/internal/R$styleable;->AndroidManifestApplication:[I
@@ -2693,8 +2915,21 @@
Lcom/android/internal/R$styleable;->AndroidManifestApplication_supportsRtl:I
Lcom/android/internal/R$styleable;->AndroidManifestApplication_theme:I
Lcom/android/internal/R$styleable;->AndroidManifestApplication_uiOptions:I
+Lcom/android/internal/R$styleable;->AndroidManifestData:[I
Lcom/android/internal/R$styleable;->AndroidManifest:[I
Lcom/android/internal/R$styleable;->AndroidManifest_installLocation:I
+Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter:[I
+Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter_priority:I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData:[I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData_name:I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData_resource:I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData_value:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_enabled:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_exported:I
+Lcom/android/internal/R$styleable;->AndroidManifestService:[I
+Lcom/android/internal/R$styleable;->AndroidManifestService_name:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_permission:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_process:I
Lcom/android/internal/R$styleable;->AndroidManifest_sharedUserId:I
Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission:[I
Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission_name:I
@@ -2708,12 +2943,26 @@
Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOff:I
Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOn:I
Lcom/android/internal/R$styleable;->CompoundButton_button:I
+Lcom/android/internal/R$styleable;->CompoundButton_checked:I
Lcom/android/internal/R$styleable;->CompoundButton:[I
+Lcom/android/internal/R$styleable;->DialogPreference_dialogTitle:I
+Lcom/android/internal/R$styleable;->DialogPreference:[I
Lcom/android/internal/R$styleable;->EdgeEffect_colorEdgeEffect:I
Lcom/android/internal/R$styleable;->EdgeEffect:[I
Lcom/android/internal/R$styleable;->IconMenuView:[I
Lcom/android/internal/R$styleable;->ImageView:[I
Lcom/android/internal/R$styleable;->ImageView_src:I
+Lcom/android/internal/R$styleable;->ListPreference_entries:I
+Lcom/android/internal/R$styleable;->ListPreference:[I
+Lcom/android/internal/R$styleable;->ListView_dividerHeight:I
+Lcom/android/internal/R$styleable;->ListView_divider:I
+Lcom/android/internal/R$styleable;->ListView_entries:I
+Lcom/android/internal/R$styleable;->ListView_footerDividersEnabled:I
+Lcom/android/internal/R$styleable;->ListView_headerDividersEnabled:I
+Lcom/android/internal/R$styleable;->ListView:[I
+Lcom/android/internal/R$styleable;->ListView_overScrollFooter:I
+Lcom/android/internal/R$styleable;->ListView_overScrollHeader:I
+Lcom/android/internal/R$styleable;->PopupWindow:[I
Lcom/android/internal/R$styleable;->Preference_defaultValue:I
Lcom/android/internal/R$styleable;->Preference_dependency:I
Lcom/android/internal/R$styleable;->Preference_enabled:I
@@ -2742,12 +2991,15 @@
Lcom/android/internal/R$styleable;->SyncAdapter_supportsUploading:I
Lcom/android/internal/R$styleable;->SyncAdapter_userVisible:I
Lcom/android/internal/R$styleable;->TabWidget:[I
+Lcom/android/internal/R$styleable;->TextAppearance:[I
Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
Lcom/android/internal/R$styleable;->TextView_drawableRight:I
Lcom/android/internal/R$styleable;->TextView_drawableTop:I
Lcom/android/internal/R$styleable;->TextView:[I
Lcom/android/internal/R$styleable;->TextView_maxLines:I
+Lcom/android/internal/R$styleable;->TextView_textColorHint:I
+Lcom/android/internal/R$styleable;->TextView_textColor:I
Lcom/android/internal/R$styleable;->View_background:I
Lcom/android/internal/R$styleable;->ViewGroup_Layout:[I
Lcom/android/internal/R$styleable;->ViewGroup_Layout_layout_height:I
@@ -2768,23 +3020,35 @@
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V
Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V
Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z
Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z
Lcom/android/internal/telephony/ITelephony;->endCall()Z
+Lcom/android/internal/telephony/ITelephony;->getCallState()I
+Lcom/android/internal/telephony/ITelephony;->getDataState()I
Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
+Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
+Lcom/android/internal/telephony/SmsRawData;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/telephony/SmsRawData;-><init>([B)V
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/util/AsyncChannel;->sendMessage(III)V
Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
+Lcom/android/internal/util/XmlUtils;->skipCurrentTag(Lorg/xmlpull/v1/XmlPullParser;)V
+Lcom/android/internal/util/XmlUtils;->writeMapXml(Ljava/util/Map;Ljava/io/OutputStream;)V
Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;->getEnabledInputMethodList()Ljava/util/List;
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/view/menu/MenuBuilder;-><init>(Landroid/content/Context;)V
Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context;
Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V
@@ -2840,6 +3104,7 @@
Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V
Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V
Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V
+Lcom/android/org/conscrypt/ConscryptSocketBase;->socket:Ljava/net/Socket;
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2862,10 +3127,12 @@
Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
Ldalvik/system/BlockGuard$Policy;->onNetwork()V
+Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V
Ldalvik/system/CloseGuard;->close()V
Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
Ldalvik/system/CloseGuard;->warnIfOpen()V
+Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String;
Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
Ldalvik/system/DexFile;->isBackedByOatFile()Z
Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
@@ -2874,6 +3141,7 @@
Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object;
Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V
+Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader;
Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V
@@ -2894,6 +3162,7 @@
Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
Ldalvik/system/VMRuntime;->clearGrowthLimit()V
Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
+Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
Ldalvik/system/VMRuntime;->is64Bit()Z
Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
@@ -2911,7 +3180,9 @@
Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
Ljava/io/FileDescriptor;->descriptor:I
Ljava/io/FileDescriptor;->getInt$()I
+Ljava/io/FileDescriptor;->isSocket$()Z
Ljava/io/FileDescriptor;->setInt$(I)V
+Ljava/io/File;->fs:Ljava/io/FileSystem;
Ljava/io/FileInputStream;->fd:Ljava/io/FileDescriptor;
Ljava/io/FileOutputStream;->fd:Ljava/io/FileDescriptor;
Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
@@ -2921,8 +3192,10 @@
Ljava/lang/Boolean;->value:Z
Ljava/lang/Byte;->value:B
Ljava/lang/Character;->value:C
+Ljava/lang/Class;->accessFlags:I
Ljava/lang/Class;->dexCache:Ljava/lang/Object;
Ljava/lang/Class;->dexClassDefIndex:I
+Ljava/lang/Class;->ifTable:[Ljava/lang/Object;
Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
Ljava/lang/Daemons$Daemon;->isRunning()Z
Ljava/lang/Daemons$Daemon;->start()V
@@ -2955,6 +3228,7 @@
Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
Ljava/lang/Short;->value:S
Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([II[III)V
Ljava/lang/System;-><init>()V
Ljava/lang/Thread;->daemon:Z
Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
@@ -2993,6 +3267,7 @@
Ljava/net/Inet6Address;-><init>()V
Ljava/net/InetAddress;->clearDnsCache()V
Ljava/net/InetAddress;->holder:Ljava/net/InetAddress$InetAddressHolder;
+Ljava/net/InetAddress;->holder()Ljava/net/InetAddress$InetAddressHolder;
Ljava/net/InetAddress$InetAddressHolder;->address:I
Ljava/net/InetAddress$InetAddressHolder;->family:I
Ljava/net/InetAddress$InetAddressHolder;->hostName:Ljava/lang/String;
@@ -3001,6 +3276,8 @@
Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
Ljava/net/Socket;->getFileDescriptor$()Ljava/io/FileDescriptor;
Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
+Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
+Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
Ljava/net/URI;->host:Ljava/lang/String;
Ljava/net/URL;->handler:Ljava/net/URLStreamHandler;
Ljava/net/URL;->handlers:Ljava/util/Hashtable;
@@ -3035,11 +3312,16 @@
Ljava/util/Collections$UnmodifiableCollection;->c:Ljava/util/Collection;
Ljava/util/Collections$UnmodifiableMap;->m:Ljava/util/Map;
Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z
+Ljava/util/concurrent/Executors$RunnableAdapter;->task:Ljava/lang/Runnable;
Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
+Ljava/util/concurrent/FutureTask;->EXCEPTIONAL:I
+Ljava/util/concurrent/FutureTask;->outcome:Ljava/lang/Object;
+Ljava/util/concurrent/FutureTask;->state:I
Ljava/util/concurrent/LinkedBlockingQueue;->capacity:I
Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
Ljava/util/HashMap$HashIterator;->hasNext()Z
+Ljava/util/jar/JarFile;->manifest:Ljava/util/jar/Manifest;
Ljava/util/LinkedHashMap;->eldest()Ljava/util/Map$Entry;
Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z
Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
@@ -3062,9 +3344,15 @@
Ljava/util/zip/ZipFile;->jzfile:J
Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
Llibcore/util/ZoneInfo;->mTransitions:[J
Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>()V
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I
Lorg/json/JSONArray;->values:Ljava/util/List;
+Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
+Lorg/json/JSONObject;->keySet()Ljava/util/Set;
Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
+Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node;
Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 77bb0bb..61ae6e7 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -32,7 +32,6 @@
Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->requestBugReport(I)V
Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
@@ -108,11 +107,6 @@
Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;Landroid/graphics/Region$Op;)Z
Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;)Z
-Landroid/graphics/Canvas;->save(I)I
-Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
-Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
-Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
-Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
Landroid/graphics/drawable/Drawable;->isProjected()Z
Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
diff --git a/core/java/android/app/AppComponentFactory.java b/core/java/android/app/AppComponentFactory.java
index 4df7379..cfaeec9 100644
--- a/core/java/android/app/AppComponentFactory.java
+++ b/core/java/android/app/AppComponentFactory.java
@@ -36,6 +36,10 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
/**
* @hide
*/
- public static AppComponentFactory DEFAULT = new AppComponentFactory();
+ public static final AppComponentFactory DEFAULT = new AppComponentFactory();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f5e138c..71b88fa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1713,6 +1713,9 @@
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
return PackageManager.PERMISSION_GRANTED;
}
+ Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+ + permission);
+ return PackageManager.PERMISSION_DENIED;
}
try {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 30f2697..4a7cf62 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -463,7 +463,11 @@
/**
* Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
- * notifications posted to this channel.
+ * notifications posted to this channel. Note: This value might be >
+ * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will
+ * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked.
+ * See {@link NotificationChannelGroup#isBlocked()} and
+ * {@link NotificationManager#areNotificationsEnabled()}.
*/
public int getImportance() {
return mImportance;
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 16166f7..0fa3c7f 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -145,7 +145,9 @@
/**
* Returns whether or not notifications posted to {@link NotificationChannel channels} belonging
- * to this group are blocked.
+ * to this group are blocked. This value is independent of
+ * {@link NotificationManager#areNotificationsEnabled()} and
+ * {@link NotificationChannel#getImportance()}.
*/
public boolean isBlocked() {
return mBlocked;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 8f01685..bd4933a 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -47,6 +46,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
+
import libcore.io.IoUtils;
import java.io.IOException;
@@ -876,15 +876,35 @@
}
/**
+ * Grants a runtime permission to a package.
+ * @param packageName The package to which to grant.
+ * @param permission The permission to grant.
+ * @throws SecurityException if unable to grant the permission.
+ */
+ public void grantRuntimePermission(String packageName, String permission) {
+ grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+ }
+
+ /**
+ * @deprecated replaced by
+ * {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
+ * @hide
+ */
+ @Deprecated
+ @TestApi
+ public boolean grantRuntimePermission(String packageName, String permission,
+ UserHandle userHandle) {
+ grantRuntimePermissionAsUser(packageName, permission, userHandle);
+ return true;
+ }
+
+ /**
* Grants a runtime permission to a package for a user.
* @param packageName The package to which to grant.
* @param permission The permission to grant.
- * @return Whether granting succeeded.
- *
- * @hide
+ * @throws SecurityException if unable to grant the permission.
*/
- @TestApi
- public boolean grantRuntimePermission(String packageName, String permission,
+ public void grantRuntimePermissionAsUser(String packageName, String permission,
UserHandle userHandle) {
synchronized (mLock) {
throwIfNotConnectedLocked();
@@ -896,25 +916,42 @@
// Calling out without a lock held.
mUiAutomationConnection.grantRuntimePermission(packageName,
permission, userHandle.getIdentifier());
- // TODO: The package manager API should return boolean.
- return true;
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error granting runtime permission", re);
+ } catch (Exception e) {
+ throw new SecurityException("Error granting runtime permission", e);
}
- return false;
}
/**
- * Revokes a runtime permission from a package for a user.
- * @param packageName The package from which to revoke.
- * @param permission The permission to revoke.
- * @return Whether revoking succeeded.
- *
+ * Revokes a runtime permission from a package.
+ * @param packageName The package to which to grant.
+ * @param permission The permission to grant.
+ * @throws SecurityException if unable to revoke the permission.
+ */
+ public void revokeRuntimePermission(String packageName, String permission) {
+ revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+ }
+
+ /**
+ * @deprecated replaced by
+ * {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
* @hide
*/
+ @Deprecated
@TestApi
public boolean revokeRuntimePermission(String packageName, String permission,
UserHandle userHandle) {
+ revokeRuntimePermissionAsUser(packageName, permission, userHandle);
+ return true;
+ }
+
+ /**
+ * Revokes a runtime permission from a package.
+ * @param packageName The package to which to grant.
+ * @param permission The permission to grant.
+ * @throws SecurityException if unable to revoke the permission.
+ */
+ public void revokeRuntimePermissionAsUser(String packageName, String permission,
+ UserHandle userHandle) {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
@@ -925,12 +962,9 @@
// Calling out without a lock held.
mUiAutomationConnection.revokeRuntimePermission(packageName,
permission, userHandle.getIdentifier());
- // TODO: The package manager API should return boolean.
- return true;
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error revoking runtime permission", re);
+ } catch (Exception e) {
+ throw new SecurityException("Error granting runtime permission", e);
}
- return false;
}
/**
@@ -949,6 +983,7 @@
synchronized (mLock) {
throwIfNotConnectedLocked();
}
+ warnIfBetterCommand(command);
ParcelFileDescriptor source = null;
ParcelFileDescriptor sink = null;
@@ -991,6 +1026,7 @@
synchronized (mLock) {
throwIfNotConnectedLocked();
}
+ warnIfBetterCommand(command);
ParcelFileDescriptor source_read = null;
ParcelFileDescriptor sink_read = null;
@@ -1056,6 +1092,16 @@
}
}
+ private void warnIfBetterCommand(String cmd) {
+ if (cmd.startsWith("pm grant ")) {
+ Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
+ + "is more robust and should be used instead of 'pm grant'");
+ } else if (cmd.startsWith("pm revoke ")) {
+ Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
+ + "is more robust and should be used instead of 'pm revoke'");
+ }
+ }
+
private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
public IAccessibilityServiceClientImpl(Looper looper) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 106b42f..b64aae5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -124,6 +124,7 @@
@SystemService(Context.DEVICE_POLICY_SERVICE)
@RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
public class DevicePolicyManager {
+
private static String TAG = "DevicePolicyManager";
private final Context mContext;
@@ -1751,6 +1752,25 @@
public static final int ID_TYPE_MEID = 8;
/**
+ * Specifies that the calling app should be granted access to the installed credentials
+ * immediately. Otherwise, access to the credentials will be gated by user approval.
+ * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+ *
+ * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+ */
+ public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1;
+
+ /**
+ * Specifies that a user can select the key via the Certificate Selection prompt.
+ * If this flag is not set when calling {@link #installKeyPair}, the key can only be granted
+ * access by implementing {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+ * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+ *
+ * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+ */
+ public static final int INSTALLKEY_SET_USER_SELECTABLE = 2;
+
+ /**
* Broadcast action: sent when the profile owner is set, changed or cleared.
*
* This broadcast is sent only to the user managed by the new profile owner.
@@ -2727,110 +2747,6 @@
}
/**
- * 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
@@ -4126,7 +4042,11 @@
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
- return installKeyPair(admin, privKey, certs, alias, requestAccess, true);
+ int flags = INSTALLKEY_SET_USER_SELECTABLE;
+ if (requestAccess) {
+ flags |= INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+ }
+ return installKeyPair(admin, privKey, certs, alias, flags);
}
/**
@@ -4150,13 +4070,9 @@
* {@link android.security.KeyChain#getCertificateChain}.
* @param alias The private key alias under which to install the certificate. If a certificate
* with that alias already exists, it will be overwritten.
- * @param requestAccess {@code true} to request that the calling app be granted access to the
- * credentials immediately. Otherwise, access to the credentials will be gated by user
- * approval.
- * @param isUserSelectable {@code true} to indicate that a user can select this key via the
- * Certificate Selection prompt, false to indicate that this key can only be granted
- * access by implementing
- * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+ * @param flags Flags to request that the calling app be granted access to the credentials
+ * and set the key to be user-selectable. See {@link #INSTALLKEY_SET_USER_SELECTABLE} and
+ * {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
* @return {@code true} if the keys were installed, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
@@ -4165,9 +4081,12 @@
* @see #DELEGATION_CERT_INSTALL
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
- @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess,
- boolean isUserSelectable) {
+ @NonNull Certificate[] certs, @NonNull String alias, int flags) {
throwIfParentInstance("installKeyPair");
+ boolean requestAccess = (flags & INSTALLKEY_REQUEST_CREDENTIALS_ACCESS)
+ == INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+ boolean isUserSelectable = (flags & INSTALLKEY_SET_USER_SELECTABLE)
+ == INSTALLKEY_SET_USER_SELECTABLE;
try {
final byte[] pemCert = Credentials.convertToPem(certs[0]);
byte[] pemChain = null;
@@ -4246,6 +4165,8 @@
* algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
* or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
* {@code keySpec} does not contain an attestation challenge.
+ * @throws UnsupportedOperationException if Device ID attestation was requested but the
+ * underlying hardware does not support it.
* @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
*/
public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
@@ -7301,11 +7222,12 @@
public @interface SystemSettingsWhitelist {}
/**
- * Called by device owner to update {@link android.provider.Settings.System} settings.
- * Validation that the value of the setting is in the correct form for the setting type should
- * be performed by the caller.
+ * Called by a device or profile owner to update {@link android.provider.Settings.System}
+ * settings. Validation that the value of the setting is in the correct form for the setting
+ * type should be performed by the caller.
* <p>
- * The settings that can be updated with this method are:
+ * The settings that can be updated by a device owner or profile owner of secondary user with
+ * this method are:
* <ul>
* <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS}</li>
* <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS_MODE}</li>
@@ -7317,7 +7239,7 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param setting The name of the setting to update.
* @param value The value to update the setting to.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setSystemSetting(@NonNull ComponentName admin,
@NonNull @SystemSettingsWhitelist String setting, String value) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 4b39a9a..37508cd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -79,10 +79,6 @@
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/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 74e3c3e..a2aaf12 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -25,7 +25,8 @@
void unpinSlice(String pkg, in Uri uri, in IBinder token);
boolean hasSliceAccess(String pkg);
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
- int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
+ int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+ in String[] autoGrantPermissions);
void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
Uri[] getPinnedSlices(String pkg);
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 95bb1f6..fc3b38d 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -70,6 +70,7 @@
HINT_ERROR,
HINT_TTL,
HINT_LAST_UPDATED,
+ HINT_PERMISSION_REQUEST,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SliceHint {}
@@ -184,6 +185,11 @@
*/
public static final String HINT_LAST_UPDATED = "last_updated";
/**
+ * A hint to indicate that this slice represents a permission request for showing
+ * slices.
+ */
+ public static final String HINT_PERMISSION_REQUEST = "permission_request";
+ /**
* Key to retrieve an extra added to an intent when a control is changed.
*/
public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 1afe53d..d5a98a5 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -81,6 +81,17 @@
* 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";
@@ -257,16 +268,17 @@
* <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
@@ -392,7 +404,8 @@
* Does the permission check to see if a caller has access to a specific slice.
* @hide
*/
- public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+ public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
+ String[] autoGrantPermissions) {
try {
if (UserHandle.isSameApp(uid, Process.myUid())) {
return;
@@ -400,7 +413,7 @@
if (pkg == null) {
throw new SecurityException("No pkg specified");
}
- int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+ int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
if (result == PERMISSION_DENIED) {
throw new SecurityException("User " + uid + " does not have slice permission for "
+ uri + ".");
@@ -412,6 +425,8 @@
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ // Notify a change has happened because we just granted a permission.
+ mContext.getContentResolver().notifyChange(uri, null);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index bf856b7..fe5742d 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -149,10 +149,31 @@
private static final boolean DEBUG = false;
private static final long SLICE_BIND_ANR = 2000;
+ private final String[] mAutoGrantPermissions;
private String mCallback;
private SliceManager mSliceManager;
+ /**
+ * A version of constructing a SliceProvider that allows autogranting slice permissions
+ * to apps that hold specific platform permissions.
+ * <p>
+ * When an app tries to bind a slice from this provider that it does not have access to,
+ * This provider will check if the caller holds permissions to any of the autoGrantPermissions
+ * specified, if they do they will be granted persisted uri access to all slices of this
+ * provider.
+ *
+ * @param autoGrantPermissions List of permissions that holders are auto-granted access
+ * to slices.
+ */
+ public SliceProvider(@NonNull String... autoGrantPermissions) {
+ mAutoGrantPermissions = autoGrantPermissions;
+ }
+
+ public SliceProvider() {
+ mAutoGrantPermissions = new String[0];
+ }
+
@Override
public void attachInfo(Context context, ProviderInfo info) {
super.attachInfo(context, info);
@@ -402,7 +423,7 @@
: getContext().getPackageManager().getNameForUid(callingUid);
try {
mSliceManager.enforceSlicePermission(sliceUri, pkg,
- callingPid, callingUid);
+ callingPid, callingUid, mAutoGrantPermissions);
} catch (SecurityException e) {
return createPermissionSlice(getContext(), sliceUri, pkg);
}
@@ -428,15 +449,17 @@
} finally {
Handler.getMain().removeCallbacks(mAnr);
}
- return new Slice.Builder(sliceUri)
- .addAction(action,
- new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
- .addText(getPermissionString(context, callingPackage), null,
- Collections.emptyList())
- .build(),
- null)
- .addHints(Arrays.asList(Slice.HINT_LIST_ITEM))
- .build();
+ Slice.Builder parent = new Slice.Builder(sliceUri);
+ Slice.Builder childAction = new Slice.Builder(parent)
+ .addHints(Arrays.asList(Slice.HINT_TITLE, Slice.HINT_SHORTCUT))
+ .addAction(action, new Slice.Builder(parent).build(), null);
+
+ parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+ .addText(getPermissionString(context, callingPackage), null,
+ Collections.emptyList())
+ .addSubSlice(childAction.build(), null)
+ .build(), null);
+ return parent.addHints(Arrays.asList(Slice.HINT_PERMISSION_REQUEST)).build();
}
/**
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index da36157..e315e91 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -24,7 +24,6 @@
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.IntArray;
import android.util.Log;
@@ -98,9 +97,8 @@
/** @hide */
NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
- long endTimestamp) throws RemoteException, SecurityException {
- final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ long endTimestamp, INetworkStatsService statsService)
+ throws RemoteException, SecurityException {
// Open network stats session
mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
mCloseGuard.open("close");
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 5576e86..2357637 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -37,6 +37,8 @@
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Provides access to network usage history and statistics. Usage data is collected in
* discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
@@ -107,9 +109,15 @@
* {@hide}
*/
public NetworkStatsManager(Context context) throws ServiceNotFoundException {
+ this(context, INetworkStatsService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public NetworkStatsManager(Context context, INetworkStatsService service) {
mContext = context;
- mService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
+ mService = service;
setPollOnOpen(true);
}
@@ -135,7 +143,8 @@
public Bucket querySummaryForDevice(NetworkTemplate template,
long startTime, long endTime) throws SecurityException, RemoteException {
Bucket bucket = null;
- NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+ NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime,
+ mService);
bucket = stats.getDeviceSummaryForNetwork();
stats.close();
@@ -208,7 +217,7 @@
}
NetworkStats stats;
- stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+ stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
stats.startSummaryEnumeration();
stats.close();
@@ -245,7 +254,7 @@
}
NetworkStats result;
- result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+ result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startSummaryEnumeration();
return result;
@@ -295,7 +304,7 @@
NetworkStats result;
try {
- result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+ result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startHistoryEnumeration(uid, tag);
} catch (RemoteException e) {
Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e);
@@ -341,7 +350,7 @@
}
NetworkStats result;
- result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+ result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startUserUidEnumeration();
return result;
}
@@ -451,19 +460,20 @@
}
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
- NetworkTemplate template = null;
+ final NetworkTemplate template;
switch (networkType) {
- case ConnectivityManager.TYPE_MOBILE: {
- template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
- } break;
- case ConnectivityManager.TYPE_WIFI: {
+ case ConnectivityManager.TYPE_MOBILE:
+ template = subscriberId == null
+ ? NetworkTemplate.buildTemplateMobileWildcard()
+ : NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ break;
+ case ConnectivityManager.TYPE_WIFI:
template = NetworkTemplate.buildTemplateWifiWildcard();
- } break;
- default: {
+ break;
+ default:
throw new IllegalArgumentException("Cannot create template for network type "
+ networkType + ", subscriberId '"
+ NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
- }
}
return template;
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index d272652..eafe91a 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -183,10 +183,13 @@
public static final int REASON_SUB_USAGE_SLICE_PINNED = 0x0009;
/** @hide */
public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV = 0x000A;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000B;
/** @hide */
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
+
/** @hide */
@IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
STANDBY_BUCKET_EXEMPTED,
@@ -665,6 +668,9 @@
case REASON_SUB_USAGE_SLICE_PINNED_PRIV:
sb.append("slpp");
break;
+ case REASON_SUB_USAGE_EXEMPTED_SYNC_START:
+ sb.append("es");
+ break;
}
break;
}
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 09ced26..b8628a4 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -243,4 +243,12 @@
*/
public abstract void reportAppJobState(String packageName, @UserIdInt int userId,
int numDeferredJobs, long timeSinceLastJobRun);
+
+ /**
+ * Report a sync that was scheduled by an active app is about to be executed.
+ *
+ * @param packageName name of the package that owns the sync adapter.
+ * @param userId which user the app is associated with
+ */
+ public abstract void reportExemptedSyncStart(String packageName, @UserIdInt int userId);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 440103a..9f3df37 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -166,24 +166,13 @@
public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
/**
- * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
- * a foreground app.
+ * {@hide} Integer extra containing a SyncExemption flag.
*
* Only the system and the shell user can set it.
*
* This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
*/
- public static final String SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC = "force_fg_sync";
-
- /**
- * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
- * a background app.
- *
- * Only the system and the shell user can set it.
- *
- * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
- */
- public static final String SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC = "force_bg_sync";
+ public static final String SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG = "v_exemption";
/**
* Set by the SyncManager to request that the SyncAdapter initialize itself for
@@ -525,6 +514,38 @@
*/
public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
+ /**
+ * No exception, throttled by app standby normally.
+ * @hide
+ */
+ public static final int SYNC_EXEMPTION_NONE = 0;
+
+ /**
+ * When executing a sync with this exemption, we'll put the target app in the ACTIVE bucket
+ * for 10 minutes. This will allow the sync adapter to schedule/run further syncs and jobs.
+ *
+ * Note this will still *not* let RARE apps to run syncs, because they still won't get network
+ * connection.
+ * @hide
+ */
+ public static final int SYNC_EXEMPTION_ACTIVE = 1;
+
+ /**
+ * In addition to {@link #SYNC_EXEMPTION_ACTIVE}, we put the sync adapter app in the
+ * temp whitelist for 10 minutes, so that even RARE apps can run syncs right away.
+ * @hide
+ */
+ public static final int SYNC_EXEMPTION_ACTIVE_WITH_TEMP = 2;
+
+ /** @hide */
+ @IntDef(flag = false, prefix = { "SYNC_EXEMPTION_" }, value = {
+ SYNC_EXEMPTION_NONE,
+ SYNC_EXEMPTION_ACTIVE,
+ SYNC_EXEMPTION_ACTIVE_WITH_TEMP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SyncExemption {}
+
// Always log queries which take 500ms+; shorter queries are
// sampled accordingly.
private static final boolean ENABLE_CONTENT_SAMPLE = false;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a3b2d22..efb9517 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -535,6 +535,19 @@
}
/**
+ * Set the level of color saturation to apply to the display.
+ * @param level The amount of saturation to apply, between 0 and 1 inclusive.
+ * 0 produces a grayscale image, 1 is normal.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
+ public void setSaturationLevel(float level) {
+ mGlobal.setSaturationLevel(level);
+ }
+
+ /**
* Creates a virtual display.
*
* @see #createVirtualDisplay(String, int, int, int, Surface, int,
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1f67a6b..2d0ef2f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -384,6 +384,17 @@
}
}
+ /**
+ * Set the level of color saturation to apply to the display.
+ */
+ public void setSaturationLevel(float level) {
+ try {
+ mDm.setSaturationLevel(level);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
String name, int width, int height, int densityDpi, Surface surface, int flags,
VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 9fcb9d3..b77de748 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -65,6 +65,9 @@
// Requires CONFIGURE_DISPLAY_COLOR_MODE
void requestColorMode(int displayId, int colorMode);
+ // Requires CONTROL_DISPLAY_SATURATION
+ void setSaturationLevel(float level);
+
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
// MediaProjection token for certain combinations of flags.
int createVirtualDisplay(in IVirtualDisplayCallback callback,
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index b2aa9ba..e6f523c 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -263,6 +263,17 @@
/**
* @hide for framework use only
*/
+ public Filter() {
+ mIdentifierTypes = Collections.emptySet();
+ mIdentifiers = Collections.emptySet();
+ mIncludeCategories = false;
+ mExcludeModifications = false;
+ mVendorFilter = null;
+ }
+
+ /**
+ * @hide for framework use only
+ */
public Filter(@Nullable Map<String, String> vendorFilter) {
mIdentifierTypes = Collections.emptySet();
mIdentifiers = Collections.emptySet();
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 8fde82e..8263bb8 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -211,6 +211,7 @@
private final String mSerial;
private final int mNumTuners;
private final int mNumAudioSources;
+ private final boolean mIsInitializationRequired;
private final boolean mIsCaptureSupported;
private final BandDescriptor[] mBands;
private final boolean mIsBgScanSupported;
@@ -222,7 +223,8 @@
/** @hide */
public ModuleProperties(int id, String serviceName, int classId, String implementor,
String product, String version, String serial, int numTuners, int numAudioSources,
- boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+ boolean isInitializationRequired, boolean isCaptureSupported,
+ BandDescriptor[] bands, boolean isBgScanSupported,
@ProgramSelector.ProgramType int[] supportedProgramTypes,
@ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
@Nullable Map<String, Integer> dabFrequencyTable,
@@ -236,6 +238,7 @@
mSerial = serial;
mNumTuners = numTuners;
mNumAudioSources = numAudioSources;
+ mIsInitializationRequired = isInitializationRequired;
mIsCaptureSupported = isCaptureSupported;
mBands = bands;
mIsBgScanSupported = isBgScanSupported;
@@ -329,6 +332,18 @@
return mNumAudioSources;
}
+ /**
+ * Checks, if BandConfig initialization (after {@link RadioManager#openTuner})
+ * is required to be done before other operations or not.
+ *
+ * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged}
+ * callback before executing any other operations. Otherwise, such operation will fail
+ * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code.
+ */
+ public boolean isInitializationRequired() {
+ return mIsInitializationRequired;
+ }
+
/** {@code true} if audio capture is possible from radio tuner output.
* This indicates if routing to audio devices not connected to the same HAL as the FM radio
* is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
@@ -419,6 +434,7 @@
mSerial = in.readString();
mNumTuners = in.readInt();
mNumAudioSources = in.readInt();
+ mIsInitializationRequired = in.readInt() == 1;
mIsCaptureSupported = in.readInt() == 1;
Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
mBands = new BandDescriptor[tmp.length];
@@ -454,6 +470,7 @@
dest.writeString(mSerial);
dest.writeInt(mNumTuners);
dest.writeInt(mNumAudioSources);
+ dest.writeInt(mIsInitializationRequired ? 1 : 0);
dest.writeInt(mIsCaptureSupported ? 1 : 0);
dest.writeParcelableArray(mBands, flags);
dest.writeInt(mIsBgScanSupported ? 1 : 0);
@@ -476,6 +493,7 @@
+ ", mVersion=" + mVersion + ", mSerial=" + mSerial
+ ", mNumTuners=" + mNumTuners
+ ", mNumAudioSources=" + mNumAudioSources
+ + ", mIsInitializationRequired=" + mIsInitializationRequired
+ ", mIsCaptureSupported=" + mIsCaptureSupported
+ ", mIsBgScanSupported=" + mIsBgScanSupported
+ ", mBands=" + Arrays.toString(mBands) + "]";
@@ -484,8 +502,8 @@
@Override
public int hashCode() {
return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
- mSerial, mNumTuners, mNumAudioSources, mIsCaptureSupported, mBands,
- mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
+ mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
+ mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
}
@Override
@@ -503,6 +521,7 @@
if (!Objects.equals(mSerial, other.mSerial)) return false;
if (mNumTuners != other.mNumTuners) return false;
if (mNumAudioSources != other.mNumAudioSources) return false;
+ if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
if (!Objects.equals(mBands, other.mBands)) return false;
if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index 85f3115..be2846f 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -60,6 +60,7 @@
mLegacyListProxy.close();
mLegacyListProxy = null;
}
+ mCallback.close();
}
try {
mTuner.close();
@@ -278,6 +279,7 @@
try {
mTuner.startProgramListUpdates(filter);
} catch (UnsupportedOperationException ex) {
+ Log.i(TAG, "Program list is not supported with this hardware");
return null;
} catch (RemoteException ex) {
mCallback.setProgramListObserver(null, () -> { });
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 7437c40..0fb93e5 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -53,6 +53,12 @@
}
}
+ void close() {
+ synchronized (mLock) {
+ if (mProgramList != null) mProgramList.close();
+ }
+ }
+
void setProgramListObserver(@Nullable ProgramList programList,
@NonNull ProgramList.OnCloseListener closeListener) {
Objects.requireNonNull(closeListener);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e07f586..80b1c3d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -861,6 +861,10 @@
* You should always check {@link NetworkInfo#isConnected()} before initiating
* network traffic. This may return {@code null} when there is no default
* network.
+ * Note that if the default network is a VPN, this method will return the
+ * NetworkInfo for one of its underlying networks instead, or null if the
+ * VPN agent did not specify any. Apps interested in learning about VPNs
+ * should use {@link #getNetworkInfo(android.net.Network)} instead.
*
* @return a {@link NetworkInfo} object for the current default network
* or {@code null} if no default network is currently active
@@ -1018,7 +1022,11 @@
* which you're interested.
* @return a {@link NetworkInfo} object for the requested
* network type or {@code null} if the type is not
- * supported by the device.
+ * supported by the device. If {@code networkType} is
+ * TYPE_VPN and a VPN is active for the calling app,
+ * then this method will try to return one of the
+ * underlying networks for the VPN or null if the
+ * VPN agent didn't specify any.
*
* @deprecated This method does not support multiple connected networks
* of the same type. Use {@link #getAllNetworks} and
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 57f0588..8034bb6 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -56,7 +56,8 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
AUTH_HMAC_MD5,
AUTH_HMAC_SHA1,
AUTH_HMAC_SHA256,
+ AUTH_HMAC_SHA384,
AUTH_HMAC_SHA512,
AUTH_CRYPT_AES_GCM
})
@@ -126,11 +132,14 @@
* 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 @@
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 972b9c0..c7234e3 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -305,6 +305,19 @@
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
+ * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+ * applied transform before completion of graceful shutdown may result in the shutdown sequence
+ * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+ * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+ * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+ * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+ * sufficient to ensure shutdown.
+ *
+ * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+ * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+ * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+ * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+ *
* <h4>Rekey Procedure</h4>
*
* <p>When applying a new tranform to a socket in the outbound direction, the previous transform
@@ -373,6 +386,19 @@
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
+ * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+ * applied transform before completion of graceful shutdown may result in the shutdown sequence
+ * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+ * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+ * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+ * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+ * sufficient to ensure shutdown.
+ *
+ * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+ * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+ * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+ * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+ *
* <h4>Rekey Procedure</h4>
*
* <p>When applying a new tranform to a socket in the outbound direction, the previous transform
@@ -476,7 +502,7 @@
* 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
@@ -515,8 +541,8 @@
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/Network.java b/core/java/android/net/Network.java
index 5df168d..15a0ee5 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -26,6 +26,8 @@
import com.android.okhttp.internalandroidapi.Dns;
import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
+import libcore.io.IoUtils;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramSocket;
@@ -77,6 +79,11 @@
httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0;
private static final long httpKeepAliveDurationMs =
Long.parseLong(System.getProperty("http.keepAliveDuration", "300000")); // 5 minutes.
+ // Value used to obfuscate network handle longs.
+ // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+ // value in the native/android/net.c NDK implementation.
+ private static final long HANDLE_MAGIC = 0xcafed00dL;
+ private static final int HANDLE_MAGIC_SIZE = 32;
/**
* @hide
@@ -137,9 +144,15 @@
for (int i = 0; i < hostAddresses.length; i++) {
try {
Socket socket = createSocket();
- if (localAddress != null) socket.bind(localAddress);
- socket.connect(new InetSocketAddress(hostAddresses[i], port));
- return socket;
+ boolean failed = true;
+ try {
+ if (localAddress != null) socket.bind(localAddress);
+ socket.connect(new InetSocketAddress(hostAddresses[i], port));
+ failed = false;
+ return socket;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
} catch (IOException e) {
if (i == (hostAddresses.length - 1)) throw e;
}
@@ -156,15 +169,27 @@
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = createSocket();
- socket.bind(new InetSocketAddress(localAddress, localPort));
- socket.connect(new InetSocketAddress(address, port));
+ boolean failed = true;
+ try {
+ socket.bind(new InetSocketAddress(localAddress, localPort));
+ socket.connect(new InetSocketAddress(address, port));
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = createSocket();
- socket.connect(new InetSocketAddress(host, port));
+ boolean failed = true;
+ try {
+ socket.connect(new InetSocketAddress(host, port));
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
return socket;
}
@@ -176,7 +201,13 @@
@Override
public Socket createSocket() throws IOException {
Socket socket = new Socket();
- bindSocket(socket);
+ boolean failed = true;
+ try {
+ bindSocket(socket);
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
return socket;
}
}
@@ -335,6 +366,25 @@
}
/**
+ * Returns a {@link Network} object given a handle returned from {@link #getNetworkHandle}.
+ *
+ * @param networkHandle a handle returned from {@link #getNetworkHandle}.
+ * @return A {@link Network} object derived from {@code networkHandle}.
+ */
+ public static Network fromNetworkHandle(long networkHandle) {
+ if (networkHandle == 0) {
+ throw new IllegalArgumentException(
+ "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
+ }
+ if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
+ || networkHandle < 0) {
+ throw new IllegalArgumentException(
+ "Value passed to fromNetworkHandle() is not a network handle.");
+ }
+ return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+ }
+
+ /**
* Returns a handle representing this {@code Network}, for use with the NDK API.
*/
public long getNetworkHandle() {
@@ -356,14 +406,10 @@
// At some future date it may be desirable to realign the handle with
// Multiple Provisioning Domains API recommendations, as made by the
// IETF mif working group.
- //
- // The handleMagic value MUST be kept in sync with the corresponding
- // value in the native/android/net.c NDK implementation.
if (netId == 0) {
return 0L; // make this zero condition obvious for debugging
}
- final long handleMagic = 0xcafed00dL;
- return (((long) netId) << 32) | handleMagic;
+ return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
}
// implement the Parcelable interface
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ff5714b..374b3ab 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 @@
* 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 4f92fa6..caefd89 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 @@
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/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 402c995..116262e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -47,10 +47,10 @@
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
* and alarm is inexact.
*/
- oneway void setPullingAlarms(long timestampMs, long intervalMs);
+ oneway void setPullingAlarm(long nextPullTimeMs);
/** Cancel any repeating pulling alarm. */
- oneway void cancelPullingAlarms();
+ oneway void cancelPullingAlarm();
/**
* Register an alarm when we want to trigger subscribers at the given
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 5be72bc..094f004 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -158,7 +158,7 @@
* @hide
*/
public static boolean isCore(int uid) {
- if (uid > 0) {
+ if (uid >= 0) {
final int appId = getAppId(uid);
return appId < Process.FIRST_APPLICATION_UID;
} else {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bf20e6a..8905ad1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -756,10 +756,15 @@
}
try {
for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
- if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
+ if (vol.path != null && FileUtils.contains(vol.path, pathString)
+ && vol.type != VolumeInfo.TYPE_PUBLIC) {
// TODO: verify that emulated adopted devices have UUID of
// underlying volume
- return convert(vol.fsUuid);
+ try {
+ return convert(vol.fsUuid);
+ } catch (IllegalArgumentException e) {
+ continue;
+ }
}
}
} catch (RemoteException e) {
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 5c99f6c..9e3e386 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -312,7 +312,7 @@
* {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
*/
public File getInternalPathForUser(int userId) {
- if (type == TYPE_PUBLIC) {
+ if (type == TYPE_PUBLIC && !isVisible()) {
// TODO: plumb through cleaner path from vold
return new File(path.replace("/storage/", "/mnt/media_rw/"));
} else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 70f343e..b0367dc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7809,6 +7809,14 @@
"suppress_auto_battery_saver_suggestion";
/**
+ * List of packages, which data need to be unconditionally cleared before full restore.
+ * Type: string
+ * @hide
+ */
+ public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
+ "packages_to_clear_data_before_full_restore";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -11053,7 +11061,14 @@
*/
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 1d13335..f4dcce1 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -75,6 +75,7 @@
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 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 @@
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 d50424d..741af12 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -443,16 +443,7 @@
*/
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 e46c34c..9334aa9 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -78,23 +78,8 @@
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 @@
}
/**
- * 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 d16f3ea..5165f0c 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -75,7 +75,7 @@
* Creates instance of the class to to derive keys using salted SHA256 hash.
*
* <p>The salted SHA256 hash is computed over the concatenation of four byte strings, salt_len +
- * salt + key_material_len + key_material, where salt_len and key_material_len are one-byte, and
+ * salt + key_material_len + key_material, where salt_len and key_material_len are 4-byte, and
* denote the number of bytes for salt and key_material, respectively.
*/
public static @NonNull KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
@@ -106,7 +106,7 @@
/**
* @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 ab52d32..a006fa6 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -41,21 +41,133 @@
import java.util.Map;
/**
- * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
- * other Android devices belonging to the user. The exported keychain is protected by the user's
- * lock screen.
+ * Backs up cryptographic keys to remote secure hardware, encrypted with the user's lock screen.
*
- * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
- * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
- * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
- * After that number of incorrect guesses, the trusted hardware no longer allows access to the
- * key chain.
+ * <p>A system app with the {@link android.Manifest#RECOVER_KEYSTORE} permission may generate or
+ * import recoverable keys using this class. To generate a key, the app must call
+ * {@link #generateKey(String)} with the desired alias for the key. This returns an AndroidKeyStore
+ * reference to a 256-bit {@link javax.crypto.SecretKey}, which can be used for AES/GCM/NoPadding.
+ * In order to get the same key again at a later time, the app can call {@link #getKey(String)} with
+ * the same alias. If a key is generated in this way the key's raw material is never directly
+ * exposed to the calling app. The system app may also import key material using
+ * {@link #importKey(String, byte[])}. The app may only generate and import keys for its own
+ * {@code uid}.
*
- * <p>Only the recovery agent itself is able to create keys, so it is expected that the recovery
- * agent is itself the system app.
+ * <p>The same system app must also register a Recovery Agent to manage syncing recoverable keys to
+ * remote secure hardware. The Recovery Agent is a service that registers itself with the controller
+ * as follows:
*
- * <p>A recovery agent requires the privileged permission
- * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
+ * <ul>
+ * <li>Invokes {@link #initRecoveryService(String, byte[], byte[])}
+ * <ul>
+ * <li>The first argument is the alias of the root certificate used to verify trusted
+ * hardware modules. Each trusted hardware module must have a public key signed with this
+ * root of trust. Roots of trust must be shipped with the framework. The app can list all
+ * valid roots of trust by calling {@link #getRootCertificates()}.
+ * <li>The second argument is the UTF-8 bytes of the XML listing file. It lists the X509
+ * certificates containing the public keys of all available remote trusted hardware modules.
+ * Each of the X509 certificates can be validated against the chosen root of trust.
+ * <li>The third argument is the UTF-8 bytes of the XML signing file. The file contains a
+ * signature of the XML listing file. The signature can be validated against the chosen root
+ * of trust.
+ * </ul>
+ * <p>This will cause the controller to choose a random public key from the list. From then
+ * on the controller will attempt to sync the key chain with the trusted hardware module to whom
+ * that key belongs.
+ * <li>Invokes {@link #setServerParams(byte[])} with a byte string that identifies the device
+ * to a remote server. This server may act as the front-end to the trusted hardware modules. It
+ * is up to the Recovery Agent to decide how best to identify devices, but this could be, e.g.,
+ * based on the <a href="https://developers.google.com/instance-id/">Instance ID</a> of the
+ * system app.
+ * <li>Invokes {@link #setRecoverySecretTypes(int[])} with a list of types of secret used to
+ * secure the recoverable key chain. For now only
+ * {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} is supported.
+ * <li>Invokes {@link #setSnapshotCreatedPendingIntent(PendingIntent)} with a
+ * {@link PendingIntent} that is to be invoked whenever a new snapshot is created. Although the
+ * controller can create snapshots without the Recovery Agent registering this intent, it is a
+ * good idea to register the intent so that the Recovery Agent is able to sync this snapshot to
+ * the trusted hardware module as soon as it is available.
+ * </ul>
+ *
+ * <p>The trusted hardware module's public key MUST be generated on secure hardware with protections
+ * equivalent to those described in the
+ * <a href="https://developer.android.com/preview/features/security/ckv-whitepaper.html">Google
+ * Cloud Key Vault Service whitepaper</a>. The trusted hardware module itself must protect the key
+ * chain from brute-forcing using the methods also described in the whitepaper: i.e., it should
+ * limit the number of allowed attempts to enter the lock screen. If the number of attempts is
+ * exceeded the key material must no longer be recoverable.
+ *
+ * <p>A recoverable key chain snapshot is considered pending if any of the following conditions
+ * are met:
+ *
+ * <ul>
+ * <li>The system app mutates the key chain. i.e., generates, imports, or removes a key.
+ * <li>The user changes their lock screen.
+ * </ul>
+ *
+ * <p>Whenever the user unlocks their device, if a snapshot is pending, the Recovery Controller
+ * generates a new snapshot. It follows these steps to do so:
+ *
+ * <ul>
+ * <li>Generates a 256-bit AES key using {@link java.security.SecureRandom}. This is the
+ * Recovery Key.
+ * <li>Wraps the key material of all keys in the recoverable key chain with the Recovery Key.
+ * <li>Encrypts the Recovery Key with both the public key of the trusted hardware module and a
+ * symmetric key derived from the user's lock screen.
+ * </ul>
+ *
+ * <p>The controller then writes this snapshot to disk, and uses the {@link PendingIntent} that was
+ * set by the Recovery Agent during initialization to inform it that a new snapshot is available.
+ * The snapshot only contains keys for that Recovery Agent's {@code uid} - i.e., keys the agent's
+ * app itself generated. If multiple Recovery Agents exist on the device, each will be notified of
+ * their new snapshots, and each snapshots' keys will be only those belonging to the same
+ * {@code uid}.
+ *
+ * <p>The Recovery Agent retrieves its most recent snapshot by calling
+ * {@link #getKeyChainSnapshot()}. It syncs the snapshot to the remote server. The snapshot contains
+ * the public key used for encryption, which the server uses to forward the encrypted recovery key
+ * to the correct trusted hardware module. The snapshot also contains the server params, which are
+ * used to identify this device to the server.
+ *
+ * <p>The client uses the server params to identify a device whose key chain it wishes to restore.
+ * This may be on a different device to the device that originally synced the key chain. The client
+ * sends the server params identifying the previous device to the server. The server returns the
+ * X509 certificate identifying the trusted hardware module in which the encrypted Recovery Key is
+ * stored. It also returns some vault parameters identifying that particular Recovery Key to the
+ * trusted hardware module. And it also returns a vault challenge, which is used as part of the
+ * vault opening protocol to ensure the recovery claim is fresh. See the whitepaper for more
+ * details.
+ *
+ * <p>The key chain is recovered via a {@link RecoverySession}. A Recovery Agent creates one by
+ * invoking {@link #createRecoverySession()}. It then invokes
+ * {@link RecoverySession#start(String, CertPath, byte[], byte[], List)} with these arguments:
+ *
+ * <ul>
+ * <li>The alias of the root of trust used to verify the trusted hardware module.
+ * <li>The X509 certificate of the trusted hardware module.
+ * <li>The vault parameters used to identify the Recovery Key to the trusted hardware module.
+ * <li>The vault challenge, as issued by the trusted hardware module.
+ * <li>A list of secrets, corresponding to the secrets used to protect the key chain. At the
+ * moment this is a single {@link KeyChainProtectionParams} containing the lock screen of the
+ * device whose key chain is to be recovered.
+ * </ul>
+ *
+ * <p>This method returns a byte array containing the Recovery Claim, which can be issued to the
+ * remote trusted hardware module. It is encrypted with the trusted hardware module's public key
+ * (which has itself been certified with the root of trust). It also contains an ephemeral symmetric
+ * key generated for this recovery session, which the remote trusted hardware module uses to encrypt
+ * its responses. This is the Session Key.
+ *
+ * <p>If the lock screen provided is correct, the remote trusted hardware module decrypts one of the
+ * layers of lock-screen encryption from the Recovery Key. It then returns this key, encrypted with
+ * the Session Key to the Recovery Agent. As the Recovery Agent does not know the Session Key, it
+ * must then invoke {@link RecoverySession#recoverKeyChainSnapshot(byte[], List)} with the encrypted
+ * Recovery Key and the list of wrapped application keys. The controller then decrypts the layer of
+ * encryption provided by the Session Key, and uses the lock screen to decrypt the final layer of
+ * encryption. It then uses the Recovery Key to decrypt all of the wrapped application keys, and
+ * imports them into its own KeyStore. The Recovery Agent's app may then access these keys by
+ * calling {@link #getKey(String)}. Only this app's {@code uid} may access the keys that have been
+ * recovered.
*
* @hide
*/
@@ -465,16 +577,7 @@
@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 0690bd5..80845d9 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -157,8 +157,8 @@
* @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 @@
*
* @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 @@
}
/**
- * 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/security/keystore/recovery/TrustedRootCertificates.java b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
index 383af42..63faac3 100644
--- a/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
+++ b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
@@ -37,6 +37,40 @@
public static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS =
"GoogleCloudKeyVaultServiceV1";
+ /**
+ * Certificate used for client-side end-to-end encryption tests.
+ * When recovery controller is initialized with the certificate, recovery snapshots will only
+ * contain application keys started with {@link INSECURE_KEY_ALIAS}.
+ * Recovery snapshot will only be created if device is unlocked with password started with
+ * {@link #INSECURE_PASSWORD_PREFIX}.
+ *
+ * @hide
+ */
+ public static final String TEST_ONLY_INSECURE_CERTIFICATE_ALIAS =
+ "TEST_ONLY_INSECURE_CERTIFICATE_ALIAS";
+
+ /**
+ * TODO: Add insecure certificate to TestApi.
+ * @hide
+ */
+ public static @NonNull X509Certificate getTestOnlyInsecureCertificate() {
+ return parseBase64Certificate(TEST_ONLY_INSECURE_CERTIFICATE_BASE64);
+ }
+ /**
+ * Keys, which alias starts with the prefix are not protected if
+ * recovery agent uses {@link #TEST_ONLY_INSECURE_CERTIFICATE_ALIAS} root certificate.
+ * @hide
+ */
+ public static final String INSECURE_KEY_ALIAS_PREFIX =
+ "INSECURE_KEY_ALIAS_KEY_MATERIAL_IS_NOT_PROTECTED_";
+ /**
+ * Prefix for insecure passwords with length 14.
+ * Passwords started with the prefix are not protected if recovery agent uses
+ * {@link #TEST_ONLY_INSECURE_CERTIFICATE_ALIAS} root certificate.
+ * @hide
+ */
+ public static final String INSECURE_PASSWORD_PREFIX =
+ "INSECURE_PSWD_";
private static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64 = ""
+ "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
@@ -68,13 +102,43 @@
+ "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
+ "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
+ private static final String TEST_ONLY_INSECURE_CERTIFICATE_BASE64 = ""
+ + "MIIFMDCCAxigAwIBAgIJAIZ9/G8KQie9MA0GCSqGSIb3DQEBDQUAMCUxIzAhBgNV"
+ + "BAMMGlRlc3QgT25seSBVbnNlY3VyZSBSb290IENBMB4XDTE4MDMyODAwMzIyM1oX"
+ + "DTM4MDMyMzAwMzIyM1owJTEjMCEGA1UEAwwaVGVzdCBPbmx5IFVuc2VjdXJlIFJv"
+ + "b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGxFNzAEyzSPmw"
+ + "E5gfuBXdXq++bl9Ep62V7Xn1UiejvmS+pRHT39pf/M7sl4Zr9ezanJTrFvf9+B85"
+ + "VGehdsD32TgfEjThcqaoQCI6pKkHYsUo7FZ5n+G3eE8oabWRZJMVo3QDjnnFYp7z"
+ + "20vnpjDofI2oQyxHcb/1yep+ca1+4lIvbUp/ybhNFqhRXAMcDXo7pyH38eUQ1JdK"
+ + "Q/QlBbShpFEqx1Y6KilKfTDf7Wenqr67LkaEim//yLZjlHzn/BpuRTrpo+XmJZx1"
+ + "P9CX9LGOXTtmsaCcYgD4yijOvV8aEsIJaf1kCIO558oH0oQc+0JG5aXeLN7BDlyZ"
+ + "vH0RdSx5nQLS9kj2I6nthOw/q00/L+S6A0m5jyNZOAl1SY78p+wO0d9eHbqQzJwf"
+ + "EsSq3qGAqlgQyyjp6oxHBqT9hZtN4rxw+iq0K1S4kmTLNF1FvmIB1BE+lNvvoGdY"
+ + "5G0b6Pe4R5JFn9LV3C3PEmSYnae7iG0IQlKmRADIuvfJ7apWAVanJPJAAWh2Akfp"
+ + "8Uxr02cHoY6o7vsEhJJOeMkipaBHThESm/XeFVubQzNfZ9gjQnB9ZX2v+lyj+WYZ"
+ + "SAz3RuXx6TlLrmWccMpQDR1ibcgyyjLUtX3kwZl2OxmJXitjuD7xlxvAXYob15N+"
+ + "K4xKHgxUDrbt2zU/tY0vgepAUg/xbwIDAQABo2MwYTAdBgNVHQ4EFgQUwyeNpYgs"
+ + "XXYvh9z0/lFrja7sV+swHwYDVR0jBBgwFoAUwyeNpYgsXXYvh9z0/lFrja7sV+sw"
+ + "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQENBQAD"
+ + "ggIBAGuOsvMN5SD3RIQnMJtBpcHNrxun+QFjPZFlYCLfIPrUkHpn5O1iIIq8tVLd"
+ + "2V+12VKnToUEANsYBD3MP8XjP+6GZ7ZQ2rwLGvUABKSX4YXvmjEEXZUZp0y3tIV4"
+ + "kUDlbACzguPneZDp5Qo7YWH4orgqzHkn0sD/ikO5XrAqmzc245ewJlrf+V11mjcu"
+ + "ELfDrEejpPhi7Hk/ZNR0ftP737Hs/dNoCLCIaVNgYzBZhgo4kd220TeJu2ttW0XZ"
+ + "ldyShtpcOmyWKBgVseixR6L/3sspPHyAPXkSuRo0Eh1xvzDKCg9ttb0qoacTlXMF"
+ + "GkBpNzmVq67NWFGGa9UElift1mv6RfktPCAGZ+Ai8xUiKAUB0Eookpt/8gX9Senq"
+ + "yP/jMxkxXmHWxUu8+KnLvj6WLrfftuuD7u3cfc7j5kkrheDz3O4h4477GnqL5wdo"
+ + "9DuEsNc4FxJVz8Iy8RS6cJuW4pihYpM1Tyn7uopLnImpYzEY+R5aQqqr+q/A1diq"
+ + "ogbEKPH6oUiqJUwq3nD70gPBUKJmIzS4vLwLouqUHEm1k/MgHV/BkEU0uVHszPFa"
+ + "XUMMCHb0iT9P8LuZ7Ajer3SR/0TRVApCrk/6OV68e+6k/OFpM5kcZnNMD5ANyBri"
+ + "Tsz3NrDwSw4i4+Dsfh6A9dB/cEghw4skLaBxnQLQIgVeqCzK";
+
/**
* The X509 certificate of the trusted root CA cert for the recoverable key store service.
*
* TODO: Change it to the production certificate root CA before the final launch.
*/
private static final X509Certificate GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_CERTIFICATE =
- parseGoogleCloudKeyVaultServiceV1Certificate();
+ parseBase64Certificate(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64);
private static final int NUMBER_OF_ROOT_CERTIFICATES = 1;
@@ -107,9 +171,9 @@
return certificates;
}
- private static X509Certificate parseGoogleCloudKeyVaultServiceV1Certificate() {
+ private static X509Certificate parseBase64Certificate(String base64Certificate) {
try {
- return decodeBase64Cert(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64);
+ return decodeBase64Cert(base64Certificate);
} catch (CertificateException e) {
// Should not happen
throw new RuntimeException(e);
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index cf16749..1cd76d2 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -41,11 +41,11 @@
*
* <p>A field classification score is a {@code float} representing how well an
* {@link AutofillValue} filled matches a expected value predicted by an autofill service
- * —a full-match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
+ * —a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
*
- * <p>The exact score depends on the algorithm used to calculate it— the service must provide
+ * <p>The exact score depends on the algorithm used to calculate it—the service must provide
* at least one default algorithm (which is used when the algorithm is not specified or is invalid),
- * but it could provide more (in which case the algorithm name should be specifiied by the caller
+ * but it could provide more (in which case the algorithm name should be specified by the caller
* when calculating the scores).
*
* {@hide}
@@ -113,23 +113,67 @@
/**
* Calculates field classification scores in a batch.
*
- * <p>See {@link AutofillFieldClassificationService} for more info about field classification
- * scores.
+ * <p>A field classification score is a {@code float} representing how well an
+ * {@link AutofillValue} filled matches a expected value predicted by an autofill service
+ * —a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
*
- * @param algorithm name of the algorithm to be used to calculate the scores. If invalid, the
- * default algorithm will be used instead.
- * @param args optional arguments to be passed to the algorithm.
+ * <p>The exact score depends on the algorithm used to calculate it—the service must
+ * provide at least one default algorithm (which is used when the algorithm is not specified
+ * or is invalid), but it could provide more (in which case the algorithm name should be
+ * specified by the caller when calculating the scores).
+ *
+ * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that
+ * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to:
+ *
+ * <pre>
+ * service.onGetScores("EXACT_MATCH", null,
+ * Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+ * Arrays.asList("email1", "phone1"));
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 0.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * <p>If the same algorithm allows the caller to specify whether the comparisons should be
+ * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to:
+ *
+ * <pre>
+ * Bundle algorithmOptions = new Bundle();
+ * algorithmOptions.putBoolean("case_sensitive", false);
+ *
+ * service.onGetScores("EXACT_MATCH", algorithmOptions,
+ * Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+ * Arrays.asList("email1", "phone1"));
+ * </pre>
+ *
+ * <p>Returns:
+ *
+ * <pre>
+ * [
+ * [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+ * [0.0, 1.0] // "PHONE1" compared against ["email1", "phone1"]
+ * ];
+ * </pre>
+ *
+ * @param algorithm name of the algorithm to be used to calculate the scores. If invalid or
+ * {@code null}, the default algorithm is used instead.
+ * @param algorithmOptions optional arguments to be passed to the algorithm.
* @param actualValues values entered by the user.
* @param userDataValues values predicted from the user data.
- * @return the calculated scores, with the first dimension representing actual values and the
- * second dimension values from {@link UserData}.
+ * @return the calculated scores of {@code actualValues} x {@code userDataValues}.
*
* {@hide}
*/
@Nullable
@SystemApi
public float[][] onGetScores(@Nullable String algorithm,
- @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
+ @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
@NonNull List<String> userDataValues) {
Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
return null;
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index df62446..6e5bacf 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -424,7 +424,7 @@
* @return map map whose key is the id of the manually-entered field, and value is the
* ids of the datasets that have that value but were not selected by the user.
*/
- @Nullable public Map<AutofillId, Set<String>> getManuallyEnteredField() {
+ @NonNull public Map<AutofillId, Set<String>> getManuallyEnteredField() {
if (mManuallyFilledFieldIds == null || mManuallyFilledDatasetIds == null) {
return Collections.emptyMap();
}
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 8a7ff4d..0128710 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -144,7 +144,8 @@
}
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/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 3b8fc5c..f15e1a1 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -72,22 +72,31 @@
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 @@
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 @@
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 @@
//
// 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 @@
{
// 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/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 630007b..b413d48 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -43,8 +43,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -512,7 +512,7 @@
public static final class Options implements Parcelable {
private @Nullable LocaleList mDefaultLocales;
- private @Nullable Calendar mReferenceTime;
+ private @Nullable ZonedDateTime mReferenceTime;
public Options() {}
@@ -531,7 +531,7 @@
* be interpreted. This should usually be the time when the text was originally
* composed. If no reference time is set, now is used.
*/
- public Options setReferenceTime(Calendar referenceTime) {
+ public Options setReferenceTime(ZonedDateTime referenceTime) {
mReferenceTime = referenceTime;
return this;
}
@@ -550,7 +550,7 @@
* interpreted.
*/
@Nullable
- public Calendar getReferenceTime() {
+ public ZonedDateTime getReferenceTime() {
return mReferenceTime;
}
@@ -567,7 +567,7 @@
}
dest.writeInt(mReferenceTime != null ? 1 : 0);
if (mReferenceTime != null) {
- dest.writeSerializable(mReferenceTime);
+ dest.writeString(mReferenceTime.toString());
}
}
@@ -589,7 +589,7 @@
mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
}
if (in.readInt() > 0) {
- mReferenceTime = (Calendar) in.readSerializable();
+ mReferenceTime = ZonedDateTime.parse(in.readString());
}
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 5ba470a..8d1ed0e 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -16,6 +16,8 @@
package android.view.textclassifier;
+import static java.time.temporal.ChronoUnit.MILLIS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
@@ -45,9 +47,10 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.time.Instant;
+import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -119,7 +122,7 @@
&& rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
- final Calendar refTime = Calendar.getInstance();
+ final ZonedDateTime refTime = ZonedDateTime.now();
final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed();
final TextClassifierImplNative nativeImpl = getNative(locales);
final String string = text.toString();
@@ -143,8 +146,8 @@
nativeImpl.classifyText(
string, start, end,
new TextClassifierImplNative.ClassificationOptions(
- refTime.getTimeInMillis(),
- refTime.getTimeZone().getID(),
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
localesString));
final int size = results.length;
for (int i = 0; i < size; i++) {
@@ -183,19 +186,20 @@
final String string = text.toString();
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final String localesString = concatenateLocales(locales);
- final Calendar refTime = (options != null && options.getReferenceTime() != null)
- ? options.getReferenceTime() : Calendar.getInstance();
+ final ZonedDateTime refTime =
+ (options != null && options.getReferenceTime() != null)
+ ? options.getReferenceTime() : ZonedDateTime.now();
final TextClassifierImplNative.ClassificationResult[] results =
getNative(locales)
.classifyText(string, startIndex, endIndex,
new TextClassifierImplNative.ClassificationOptions(
- refTime.getTimeInMillis(),
- refTime.getTimeZone().getID(),
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
localesString));
if (results.length > 0) {
return createClassificationResult(
- results, string, startIndex, endIndex, refTime);
+ results, string, startIndex, endIndex, refTime.toInstant());
}
}
} catch (Throwable t) {
@@ -224,7 +228,7 @@
try {
final long startTimeMs = System.currentTimeMillis();
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
- final Calendar refTime = Calendar.getInstance();
+ final ZonedDateTime refTime = ZonedDateTime.now();
final Collection<String> entitiesToIdentify =
options != null && options.getEntityConfig() != null
? options.getEntityConfig().resolveEntityListModifications(
@@ -236,8 +240,8 @@
nativeImpl.annotate(
textString,
new TextClassifierImplNative.AnnotationOptions(
- refTime.getTimeInMillis(),
- refTime.getTimeZone().getID(),
+ refTime.toInstant().toEpochMilli(),
+ refTime.getZone().getId(),
concatenateLocales(defaultLocales)));
for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
final TextClassifierImplNative.ClassificationResult[] results =
@@ -416,7 +420,7 @@
private TextClassification createClassificationResult(
TextClassifierImplNative.ClassificationResult[] classifications,
- String text, int start, int end, @Nullable Calendar referenceTime) {
+ String text, int start, int end, @Nullable Instant referenceTime) {
final String classifiedText = text.substring(start, end);
final TextClassification.Builder builder = new TextClassification.Builder()
.setText(classifiedText);
@@ -646,7 +650,7 @@
@NonNull
public static List<LabeledIntent> create(
Context context,
- @Nullable Calendar referenceTime,
+ @Nullable Instant referenceTime,
TextClassifierImplNative.ClassificationResult classification,
String text) {
final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
@@ -663,10 +667,9 @@
case TextClassifier.TYPE_DATE:
case TextClassifier.TYPE_DATE_TIME:
if (classification.getDatetimeResult() != null) {
- Calendar eventTime = Calendar.getInstance();
- eventTime.setTimeInMillis(
+ final Instant parsedTime = Instant.ofEpochMilli(
classification.getDatetimeResult().getTimeMsUtc());
- return createForDatetime(context, type, referenceTime, eventTime);
+ return createForDatetime(context, type, referenceTime, parsedTime);
} else {
return new ArrayList<>();
}
@@ -758,18 +761,17 @@
@NonNull
private static List<LabeledIntent> createForDatetime(
- Context context, String type, @Nullable Calendar referenceTime,
- Calendar eventTime) {
+ Context context, String type, @Nullable Instant referenceTime,
+ Instant parsedTime) {
if (referenceTime == null) {
// If no reference time was given, use now.
- referenceTime = Calendar.getInstance();
+ referenceTime = Instant.now();
}
List<LabeledIntent> actions = new ArrayList<>();
- actions.add(createCalendarViewIntent(context, eventTime));
- final long millisSinceReference =
- eventTime.getTimeInMillis() - referenceTime.getTimeInMillis();
- if (millisSinceReference > MIN_EVENT_FUTURE_MILLIS) {
- actions.add(createCalendarCreateEventIntent(context, eventTime, type));
+ actions.add(createCalendarViewIntent(context, parsedTime));
+ final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
+ if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
+ actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
}
return actions;
}
@@ -784,10 +786,10 @@
}
@NonNull
- private static LabeledIntent createCalendarViewIntent(Context context, Calendar eventTime) {
+ private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
- ContentUris.appendId(builder, eventTime.getTimeInMillis());
+ ContentUris.appendId(builder, parsedTime.toEpochMilli());
return new LabeledIntent(
context.getString(com.android.internal.R.string.view_calendar),
context.getString(com.android.internal.R.string.view_calendar_desc),
@@ -796,7 +798,7 @@
@NonNull
private static LabeledIntent createCalendarCreateEventIntent(
- Context context, Calendar eventTime, @EntityType String type) {
+ Context context, Instant parsedTime, @EntityType String type) {
final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
return new LabeledIntent(
context.getString(com.android.internal.R.string.add_calendar_event),
@@ -805,9 +807,9 @@
.setData(CalendarContract.Events.CONTENT_URI)
.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
- eventTime.getTimeInMillis())
+ parsedTime.toEpochMilli())
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
- eventTime.getTimeInMillis() + DEFAULT_EVENT_DURATION));
+ parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
}
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 92f496a8..9946726 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4585,8 +4585,8 @@
return mContainer.isShowing();
}
- private boolean isVisible() {
- // Always show a dragging handle.
+ private boolean shouldShow() {
+ // A dragging handle should always be shown.
if (mIsDragging) {
return true;
}
@@ -4599,6 +4599,10 @@
mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
}
+ private void setVisible(final boolean visible) {
+ mContainer.getContentView().setVisibility(visible ? VISIBLE : INVISIBLE);
+ }
+
public abstract int getCurrentCursorOffset();
protected abstract void updateSelection(int offset);
@@ -4692,7 +4696,7 @@
onHandleMoved();
}
- if (isVisible()) {
+ if (shouldShow()) {
// Transform to the window coordinates to follow the view tranformation.
final int[] pts = { mPositionX + mHotspotX + getHorizontalOffset(), mPositionY};
mTextView.transformFromViewToWindowSpace(pts);
@@ -4745,6 +4749,15 @@
return 0;
}
+ private boolean tooLargeTextForMagnifier() {
+ final float magnifierContentHeight = Math.round(
+ mMagnifierAnimator.mMagnifier.getHeight()
+ / mMagnifierAnimator.mMagnifier.getZoom());
+ final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
+ final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
+ return glyphHeight > magnifierContentHeight;
+ }
+
/**
* Computes the position where the magnifier should be shown, relative to
* {@code mTextView}, and writes them to {@code showPosInView}. Also decides
@@ -4824,13 +4837,12 @@
return true;
}
- private boolean tooLargeTextForMagnifier() {
- final float magnifierContentHeight = Math.round(
- mMagnifierAnimator.mMagnifier.getHeight()
- / mMagnifierAnimator.mMagnifier.getZoom());
- final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
- final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
- return glyphHeight > magnifierContentHeight;
+ private boolean handleOverlapsMagnifier() {
+ final int handleY = mContainer.getDecorViewLayoutParams().y;
+ final int magnifierBottomWhenAtWindowTop =
+ mTextView.getRootWindowInsets().getSystemWindowInsetTop()
+ + mMagnifierAnimator.mMagnifier.getHeight();
+ return handleY <= magnifierBottomWhenAtWindowTop;
}
protected final void updateMagnifier(@NonNull final MotionEvent event) {
@@ -4846,6 +4858,13 @@
mRenderCursorRegardlessTiming = true;
mTextView.invalidateCursorPath();
suspendBlink();
+ // Hide handle if it overlaps the magnifier.
+ if (handleOverlapsMagnifier()) {
+ setVisible(false);
+ } else {
+ setVisible(true);
+ }
+
mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
} else {
dismissMagnifier();
@@ -4857,6 +4876,7 @@
mMagnifierAnimator.dismiss();
mRenderCursorRegardlessTiming = false;
resumeBlink();
+ setVisible(true);
}
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index b049db3..f4b7032 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -187,11 +187,27 @@
@Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
+ return performBackup(packageInfo, data, /*flags=*/ 0);
+ }
+
+ @Override
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+ boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
+ boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
+
+ if (isIncremental) {
+ Log.i(TAG, "Performing incremental backup for " + packageInfo.packageName);
+ } else if (isNonIncremental) {
+ Log.i(TAG, "Performing non-incremental backup for " + packageInfo.packageName);
+ } else {
+ Log.i(TAG, "Performing backup for " + packageInfo.packageName);
+ }
+
if (DEBUG) {
try {
- StructStat ss = Os.fstat(data.getFileDescriptor());
- Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
- + " size=" + ss.st_size);
+ StructStat ss = Os.fstat(data.getFileDescriptor());
+ Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
+ + " size=" + ss.st_size + " flags=" + flags);
} catch (ErrnoException e) {
Log.w(TAG, "Unable to stat input file in performBackup() on "
+ packageInfo.packageName);
@@ -199,7 +215,26 @@
}
File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
- packageDir.mkdirs();
+ boolean hasDataForPackage = !packageDir.mkdirs();
+
+ if (isIncremental) {
+ if (mParameters.isNonIncrementalOnly() || !hasDataForPackage) {
+ if (mParameters.isNonIncrementalOnly()) {
+ Log.w(TAG, "Transport is in non-incremental only mode.");
+
+ } else {
+ Log.w(TAG,
+ "Requested incremental, but transport currently stores no data for the "
+ + "package, requesting non-incremental retry.");
+ }
+ return TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED;
+ }
+ }
+ if (isNonIncremental && hasDataForPackage) {
+ Log.w(TAG, "Requested non-incremental, deleting existing data.");
+ clearBackupData(packageInfo);
+ packageDir.mkdirs();
+ }
// Each 'record' in the restore set is kept in its own file, named by
// the record key. Wind through the data file, extracting individual
diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/core/java/com/android/internal/backup/LocalTransportParameters.java
index 154e79d..2427d39 100644
--- a/core/java/com/android/internal/backup/LocalTransportParameters.java
+++ b/core/java/com/android/internal/backup/LocalTransportParameters.java
@@ -26,8 +26,10 @@
private static final String TAG = "LocalTransportParams";
private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
+ private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
private boolean mFakeEncryptionFlag;
+ private boolean mIsNonIncrementalOnly;
LocalTransportParameters(Handler handler, ContentResolver resolver) {
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -37,11 +39,16 @@
return mFakeEncryptionFlag;
}
+ boolean isNonIncrementalOnly() {
+ return mIsNonIncrementalOnly;
+ }
+
public String getSettingValue(ContentResolver resolver) {
return Settings.Secure.getString(resolver, SETTING);
}
public void update(KeyValueListParser parser) {
mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
+ mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
}
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 59b14f1..ae7ba19 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -61,7 +61,6 @@
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 957c784..e8fc598 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1000,6 +1000,11 @@
setPatternInProgress(false);
cancelLineAnimations();
notifyPatternDetected();
+ // Also clear pattern if fading is enabled
+ if (mFadePattern) {
+ clearPatternDrawLookup();
+ mPatternDisplayMode = DisplayMode.Correct;
+ }
invalidate();
}
if (PROFILE_DRAWING) {
@@ -1008,9 +1013,6 @@
mDrawingProfilingStarted = false;
}
}
- if (mFadePattern) {
- clearPattern();
- }
}
private void cancelLineAnimations() {
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 726c450..825b7a0 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -210,7 +210,7 @@
jint desiredWidth, jint desiredHeight, jobject jsubset,
jboolean requireMutable, jint allocator,
jboolean requireUnpremul, jboolean preferRamOverQuality,
- jboolean asAlphaMask) {
+ jboolean asAlphaMask, jobject jcolorSpace) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
SkAndroidCodec* codec = decoder->mCodec.get();
const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
@@ -264,7 +264,8 @@
// This is currently the only way to know that we should decode to F16.
colorType = codec->computeOutputColorType(colorType);
}
- sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+ sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
+ colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
SkBitmap bm;
@@ -507,18 +508,26 @@
return encodedFormatToString(env, decoder->mCodec->getEncodedFormat());
}
+static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+ auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get();
+ auto colorType = codec->computeOutputColorType(codec->getInfo().colorType());
+ sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+ return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
+}
+
static const JNINativeMethod gImageDecoderMethods[] = {
{ "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
{ "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
{ "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
{ "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
{ "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
- { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+ { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize },
{ "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
{ "nClose", "(J)V", (void*) ImageDecoder_nClose},
{ "nGetMimeType", "(J)Ljava/lang/String;", (void*) ImageDecoder_nGetMimeType },
+ { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;", (void*) ImageDecoder_nGetColorSpace },
};
int register_android_graphics_ImageDecoder(JNIEnv* env) {
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index d3d6882..97abd82 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -37,6 +37,14 @@
class SkPathGlue {
public:
+ static void finalizer(SkPath* obj) {
+ // Purge entries from the HWUI path cache if this path's data is unique
+ if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
+ android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
+ }
+ delete obj;
+ }
+
// ---------------- Regular JNI -----------------------------
static jlong init(JNIEnv* env, jclass clazz) {
@@ -48,13 +56,8 @@
return reinterpret_cast<jlong>(new SkPath(*val));
}
- static void finalize(JNIEnv* env, jclass clazz, jlong objHandle) {
- SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- // Purge entries from the HWUI path cache if this path's data is unique
- if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
- }
- delete obj;
+ static jlong getFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
}
static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) {
@@ -469,7 +472,9 @@
SkRect rect;
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
jboolean result = obj->isRect(&rect);
- GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+ if (jrect) {
+ GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+ }
return result;
}
@@ -510,7 +515,7 @@
static const JNINativeMethod methods[] = {
{"nInit","()J", (void*) SkPathGlue::init},
{"nInit","(J)J", (void*) SkPathGlue::init_Path},
- {"nFinalize", "(J)V", (void*) SkPathGlue::finalize},
+ {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer},
{"nSet","(JJ)V", (void*) SkPathGlue::set},
{"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
{"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 04cb08f..b0f68cd 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,7 +37,7 @@
#include <system/graphics.h>
#include <ui/DisplayInfo.h>
#include <ui/FrameStats.h>
-#include <ui/GraphicsTypes.h>
+#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -598,7 +598,7 @@
static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return NULL;
- Vector<ColorMode> colorModes;
+ Vector<ui::ColorMode> colorModes;
if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR ||
colorModes.isEmpty()) {
return NULL;
@@ -628,7 +628,7 @@
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return JNI_FALSE;
status_t err = SurfaceComposerClient::setActiveColorMode(token,
- static_cast<ColorMode>(colorMode));
+ static_cast<ui::ColorMode>(colorMode));
return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index b5fd792..b2853c9 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -260,7 +260,7 @@
}
// Apply system or app filter based on uid.
- if (getuid() >= AID_APP_START) {
+ if (uid >= AID_APP_START) {
set_app_seccomp_filter();
} else {
set_system_seccomp_filter();
@@ -619,11 +619,6 @@
fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
}
- // Must be called when the new process still has CAP_SYS_ADMIN. The other alternative is to
- // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
- // b/71859146).
- SetUpSeccompFilter(uid);
-
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
if (!EnableKeepCapabilities(&error_msg)) {
@@ -699,6 +694,13 @@
fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
}
+ // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+ // uid from 0, which clears capabilities. The other alternative is to call
+ // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146). As the result, privileged syscalls used below still need to be accessible in
+ // app process.
+ SetUpSeccompFilter(uid);
+
rc = setresuid(uid, uid, uid);
if (rc == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 0fea0dc..ea0b825 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -175,9 +175,9 @@
];
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/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index 694b94b..8bf3772 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -192,6 +192,8 @@
optional string libc_debug_malloc_program = 15;
message Log {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string tag_WifiHAL = 1;
optional string tag_stats_log = 2;
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index c7de947..b5303c8 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -672,8 +672,10 @@
option (android.msg_privacy).dest = DEST_LOCAL;
// The requested Private DNS mode and an accompanying specifier.
- optional SettingProto dns_mode = 1;
- optional SettingProto dns_specifier = 2;
+ // msg_privacy settings don't apply to sub messages, only to primitive
+ // fields, so these must also be explicitly set to LOCAL.
+ optional SettingProto dns_mode = 1 [ (android.privacy).dest = DEST_LOCAL ];
+ optional SettingProto dns_specifier = 2 [ (android.privacy).dest = DEST_LOCAL ];
}
optional Private private = 96;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index cfb8980..593747d 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -145,6 +145,7 @@
optional SettingProto transport = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto manager_constants = 5;
optional SettingProto local_transport_parameters = 6;
+ optional SettingProto packages_to_clear_data_before_full_restore = 7;
}
optional Backup backup = 10;
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index bb8ce81..4df3b63 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -527,9 +527,7 @@
optional int32 calling_uid = 1;
// Job IDs can technically be negative.
optional int32 job_id = 2;
- optional string battery_name = 3 [
- (.android.privacy).dest = DEST_EXPLICIT
- ];
+ optional string battery_name = 3;
}
// Dump from a com.android.server.job.controllers.JobStatus object.
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index b60c569..8240d8a 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -80,7 +80,7 @@
optional string model = 2;
optional string description = 3;
optional string version = 4;
- optional string uri = 5;
+ optional string uri = 5 [ (android.privacy).dest = DEST_EXPLICIT ];
optional string serial = 6 [ (android.privacy).dest = DEST_EXPLICIT ];
}
@@ -155,6 +155,7 @@
message UsbConnectionRecordProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ // usb device's address, e.g. 001/002, nothing about the phone
optional string device_address = 1;
optional android.service.UsbConnectionRecordMode mode = 2;
optional int64 timestamp = 3;
@@ -251,6 +252,7 @@
optional string name = 3;
optional bool has_playback = 4;
optional bool has_capture = 5;
+ // usb device's address, e.g. 001/002, nothing about the phone
optional string address = 6;
}
@@ -259,6 +261,7 @@
optional int32 card = 1;
optional int32 device = 2;
+ // usb device's address, e.g. 001/002, nothing about the phone
optional string device_address = 3;
}
diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto
index cbd06fd..2a03050 100644
--- a/core/proto/android/view/displayinfo.proto
+++ b/core/proto/android/view/displayinfo.proto
@@ -29,5 +29,5 @@
optional int32 logical_height = 2;
optional int32 app_width = 3;
optional int32 app_height = 4;
- optional string name = 5;
+ optional string name = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 48d394a..f4715fc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2016,11 +2016,11 @@
<permission android:name="android.permission.REMOVE_TASKS"
android:protectionLevel="signature" />
- <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
+ <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
android:protectionLevel="signature|privileged|development" />
- <!-- @SystemApi @hide Allows an application to embed other activities -->
+ <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
<permission android:name="android.permission.ACTIVITY_EMBEDDING"
android:protectionLevel="signature|privileged|development" />
@@ -3121,6 +3121,12 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to control the color saturation of the display.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to collect usage infomation about brightness slider changes.
<p>Not for use by third-party applications.</p>
@hide
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 351bd81..265eaaf 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -29,10 +29,7 @@
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Height of area above QQS where battery/time go -->
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">152dp</dimen>
+
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">40dip</dimen>
<!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 66d25df..2a6b331 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_vpn</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>
+ <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_battery</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_rotate">rotate</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 354880c..cbd7b4d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1670,7 +1670,6 @@
<java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" />
<java-symbol type="dimen" name="navigation_bar_width_car_mode" />
<java-symbol type="dimen" name="status_bar_height" />
- <java-symbol type="dimen" name="quick_qs_offset_height" />
<java-symbol type="dimen" name="quick_qs_total_height" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
@@ -2733,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 82af662e..20b298d 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -23,6 +23,7 @@
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 @@
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 2a3fcad..dfc99f6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -267,6 +267,7 @@
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,
@@ -587,7 +588,8 @@
Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
- Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION);
+ Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index afc4bd5..5d58f55 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -37,9 +37,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Locale;
-import java.util.TimeZone;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -163,8 +164,9 @@
@Test
public void testParcelOptions() {
- Calendar referenceTime = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);
- referenceTime.setTimeInMillis(946771200000L); // 2000-01-02
+ ZonedDateTime referenceTime = ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(946771200000L), // 2000-01-02
+ ZoneId.of("UTC"));
TextClassification.Options reference = new TextClassification.Options();
reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 5ca0ad6..00dc22e 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -21,10 +21,12 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.annotation.WorkerThread;
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
@@ -65,6 +67,16 @@
/**
* Source of the encoded image data.
+ *
+ * <p>This object references the data that will be used to decode a
+ * Drawable or Bitmap in {@link #decodeDrawable} or {@link #decodeBitmap}.
+ * Constructing a {@code Source} (with one of the overloads of
+ * {@code createSource}) can be done on any thread because the construction
+ * simply captures values. The real work is done in decodeDrawable or
+ * decodeBitmap.</p>
+ *
+ * <p>Further, a Source object can be reused with different settings, or
+ * even used simultaneously in multiple threads.</p>
*/
public static abstract class Source {
private Source() {}
@@ -120,7 +132,8 @@
int length = mBuffer.limit() - mBuffer.position();
return nCreate(mBuffer.array(), offset, length, this);
}
- return nCreate(mBuffer, mBuffer.position(), mBuffer.limit(), this);
+ ByteBuffer buffer = mBuffer.slice();
+ return nCreate(buffer, buffer.position(), buffer.limit(), this);
}
}
@@ -232,6 +245,8 @@
/**
* For backwards compatibility, this does *not* close the InputStream.
+ *
+ * Further, unlike other Sources, this one is not reusable.
*/
private static class InputStreamSource extends Source {
InputStreamSource(Resources res, InputStream is, int inputDensity) {
@@ -322,12 +337,17 @@
final Resources mResources;
final int mResId;
int mResDensity;
+ private Object mLock = new Object();
@Override
public Resources getResources() { return mResources; }
@Override
- public int getDensity() { return mResDensity; }
+ public int getDensity() {
+ synchronized (mLock) {
+ return mResDensity;
+ }
+ }
@Override
public ImageDecoder createImageDecoder() throws IOException {
@@ -336,10 +356,12 @@
// keep it alive.
InputStream is = mResources.openRawResource(mResId, value);
- if (value.density == TypedValue.DENSITY_DEFAULT) {
- mResDensity = DisplayMetrics.DENSITY_DEFAULT;
- } else if (value.density != TypedValue.DENSITY_NONE) {
- mResDensity = value.density;
+ synchronized (mLock) {
+ if (value.density == TypedValue.DENSITY_DEFAULT) {
+ mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+ } else if (value.density != TypedValue.DENSITY_NONE) {
+ mResDensity = value.density;
+ }
}
return createFromAsset((AssetInputStream) is, this);
@@ -432,6 +454,18 @@
public boolean isAnimated() {
return mDecoder.mAnimated;
}
+
+ /**
+ * If known, the color space the decoded bitmap will have. Note that the
+ * output color space is not guaranteed to be the color space the bitmap
+ * is encoded with. If not known (when the config is
+ * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error,
+ * it is set to null.
+ */
+ @Nullable
+ public ColorSpace getColorSpace() {
+ return mDecoder.getColorSpace();
+ }
};
/** @removed
@@ -443,6 +477,9 @@
/**
* Optional listener supplied to {@link #decodeDrawable} or
* {@link #decodeBitmap}.
+ *
+ * <p>This is necessary in order to change the default settings of the
+ * decode.</p>
*/
public static interface OnHeaderDecodedListener {
/**
@@ -534,6 +571,9 @@
/**
* Retrieve the {@link Source} that was interrupted.
+ *
+ * <p>This can be used for equality checking to find the Source which
+ * failed to completely decode.</p>
*/
@NonNull
public Source getSource() {
@@ -582,16 +622,17 @@
private final int mHeight;
private final boolean mAnimated;
- private int mDesiredWidth;
- private int mDesiredHeight;
- private int mAllocator = ALLOCATOR_DEFAULT;
- private boolean mRequireUnpremultiplied = false;
- private boolean mMutable = false;
- private boolean mConserveMemory = false;
- private boolean mDecodeAsAlphaMask = false;
- private Rect mCropRect;
- private Rect mOutPaddingRect;
- private Source mSource;
+ private int mDesiredWidth;
+ private int mDesiredHeight;
+ private int mAllocator = ALLOCATOR_DEFAULT;
+ private boolean mUnpremultipliedRequired = false;
+ private boolean mMutable = false;
+ private boolean mConserveMemory = false;
+ private boolean mDecodeAsAlphaMask = false;
+ private ColorSpace mDesiredColorSpace = null;
+ private Rect mCropRect;
+ private Rect mOutPaddingRect;
+ private Source mSource;
private PostProcessor mPostProcessor;
private OnPartialImageListener mOnPartialImageListener;
@@ -645,6 +686,7 @@
* @return a new Source object, which can be passed to
* {@link #decodeDrawable} or {@link #decodeBitmap}.
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull Resources res, int resId)
{
@@ -659,6 +701,7 @@
* @return a new Source object, which can be passed to
* {@link #decodeDrawable} or {@link #decodeBitmap}.
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull ContentResolver cr,
@NonNull Uri uri) {
@@ -670,6 +713,7 @@
*
* @hide
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull ContentResolver cr,
@NonNull Uri uri, @Nullable Resources res) {
@@ -679,6 +723,7 @@
/**
* Create a new {@link Source} from a file in the "assets" directory.
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
return new AssetSource(assets, fileName);
@@ -696,6 +741,7 @@
* not within data.
* @hide
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull byte[] data, int offset,
int length) throws ArrayIndexOutOfBoundsException {
@@ -714,6 +760,7 @@
* See {@link #createSource(byte[], int, int).
* @hide
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull byte[] data) {
return createSource(data, 0, data.length);
@@ -731,24 +778,35 @@
* be modified, even after the {@code AnimatedImageDrawable} is returned.
* {@code buffer}'s contents should never be modified during decode.</p>
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull ByteBuffer buffer) {
- return new ByteBufferSource(buffer.slice());
+ return new ByteBufferSource(buffer);
}
/**
* Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+ *
+ * <p>Unlike other Sources, this one cannot be reused.</p>
+ *
* @hide
*/
+ @AnyThread
+ @NonNull
public static Source createSource(Resources res, InputStream is) {
return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
}
/**
* Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+ *
+ * <p>Unlike other Sources, this one cannot be reused.</p>
+ *
* @hide
*/
+ @AnyThread
@TestApi
+ @NonNull
public static Source createSource(Resources res, InputStream is, int density) {
return new InputStreamSource(res, is, density);
}
@@ -756,6 +814,7 @@
/**
* Create a new {@link Source} from a {@link java.io.File}.
*/
+ @AnyThread
@NonNull
public static Source createSource(@NonNull File file) {
return new FileSource(file);
@@ -766,14 +825,16 @@
*
* <p>This takes an input that functions like
* {@link BitmapFactory.Options#inSampleSize}. It returns a width and
- * height that can be acheived by sampling the encoded image. Other widths
+ * height that can be achieved by sampling the encoded image. Other widths
* and heights may be supported, but will require an additional (internal)
* scaling step. Such internal scaling is *not* supported with
- * {@link #setRequireUnpremultiplied} set to {@code true}.</p>
+ * {@link #setUnpremultipliedRequired} set to {@code true}.</p>
*
* @param sampleSize Sampling rate of the encoded image.
* @return {@link android.util.Size} of the width and height after
* sampling.
+ *
+ * @hide
*/
@NonNull
public Size getSampledSize(int sampleSize) {
@@ -789,14 +850,32 @@
}
// Modifiers
+ /** @removed
+ * @deprecated Renamed to {@link #setTargetSize}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setResize(int width, int height) {
+ this.setTargetSize(width, height);
+ return this;
+ }
+
/**
- * Resize the output to have the following size.
+ * Specify the size of the output {@link Drawable} or {@link Bitmap}.
+ *
+ * <p>By default, the output size will match the size of the encoded
+ * image, which can be retrieved from the {@link ImageInfo} in
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
+ * <p>Only the last call to this or {@link #setTargetSampleSize} is
+ * respected.</p>
+ *
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*
* @param width must be greater than 0.
* @param height must be greater than 0.
- * @return this object for chaining.
*/
- public ImageDecoder setResize(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 + ")");
@@ -804,21 +883,70 @@
mDesiredWidth = width;
mDesiredHeight = height;
+ }
+
+ /** @removed
+ * @deprecated Renamed to {@link #setTargetSampleSize}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setResize(int sampleSize) {
+ this.setTargetSampleSize(sampleSize);
return this;
}
+ private int getTargetDimension(int original, int sampleSize, int computed) {
+ // Sampling will never result in a smaller size than 1.
+ if (sampleSize >= original) {
+ return 1;
+ }
+
+ // Use integer divide to find the desired size. If that is what
+ // getSampledSize computed, that is the size to use.
+ int target = original / sampleSize;
+ if (computed == target) {
+ return computed;
+ }
+
+ // If sampleSize does not divide evenly into original, the decoder
+ // may round in either direction. It just needs to get a result that
+ // is close.
+ int reverse = computed * sampleSize;
+ if (Math.abs(reverse - original) < sampleSize) {
+ // This is the size that can be decoded most efficiently.
+ return computed;
+ }
+
+ // The decoder could not get close (e.g. it is a DNG image).
+ return target;
+ }
+
/**
- * Resize based on a sample size.
+ * Set the target size with a sampleSize.
*
- * <p>This has the same effect as passing the result of
- * {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
+ * <p>By default, the output size will match the size of the encoded
+ * image, which can be retrieved from the {@link ImageInfo} in
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
+ * <p>Requests the decoder to subsample the original image, returning a
+ * smaller image to save memory. The sample size is the number of pixels
+ * in either dimension that correspond to a single pixel in the output.
+ * For example, sampleSize == 4 returns an image that is 1/4 the
+ * width/height of the original, and 1/16 the number of pixels.</p>
+ *
+ * <p>Must be greater than or equal to 1.</p>
+ *
+ * <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
+ *
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*
* @param sampleSize Sampling rate of the encoded image.
- * @return this object for chaining.
*/
- public ImageDecoder setResize(int sampleSize) {
+ public void setTargetSampleSize(int sampleSize) {
Size size = this.getSampledSize(sampleSize);
- return this.setResize(size.getWidth(), size.getHeight());
+ int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
+ int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
+ this.setTargetSize(targetWidth, targetHeight);
}
private boolean requestedResize() {
@@ -832,7 +960,7 @@
* Will typically result in a {@link Bitmap.Config#HARDWARE}
* allocation, but may be software for small images. In addition, this will
* switch to software when HARDWARE is incompatible, e.g.
- * {@link #setMutable}, {@link #setDecodeAsAlphaMask}.
+ * {@link #setMutableRequired}, {@link #setDecodeAsAlphaMaskEnabled}.
*/
public static final int ALLOCATOR_DEFAULT = 0;
@@ -855,8 +983,8 @@
* Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
*
* When this is combined with incompatible options, like
- * {@link #setMutable} or {@link #setDecodeAsAlphaMask}, {@link #decodeDrawable}
- * / {@link #decodeBitmap} will throw an
+ * {@link #setMutableRequired} or {@link #setDecodeAsAlphaMaskEnabled},
+ * {@link #decodeDrawable} / {@link #decodeBitmap} will throw an
* {@link java.lang.IllegalStateException}.
*/
public static final int ALLOCATOR_HARDWARE = 3;
@@ -871,17 +999,18 @@
/**
* Choose the backing for the pixel memory.
*
- * This is ignored for animated drawables.
+ * <p>This is ignored for animated drawables.</p>
+ *
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@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;
}
/**
@@ -905,18 +1034,35 @@
* {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
* </p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*/
- public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
- mRequireUnpremultiplied = requireUnpremultiplied;
+ public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
+ mUnpremultipliedRequired = unpremultipliedRequired;
+ }
+
+ /** @removed
+ * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
+ this.setUnpremultipliedRequired(unpremultipliedRequired);
return this;
}
/**
* Return whether the {@link Bitmap} will have unpremultiplied pixels.
*/
+ public boolean isUnpremultipliedRequired() {
+ return mUnpremultipliedRequired;
+ }
+
+ /** @removed
+ * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
+ */
+ @java.lang.Deprecated
public boolean getRequireUnpremultiplied() {
- return mRequireUnpremultiplied;
+ return this.isUnpremultipliedRequired();
}
/**
@@ -932,11 +1078,12 @@
* {@link Canvas} will be recorded immediately and then applied to each
* frame.</p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
*/
- public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
+ public void setPostProcessor(@Nullable PostProcessor p) {
mPostProcessor = p;
- return this;
}
/**
@@ -953,11 +1100,12 @@
* <p>Will be called if there is an error in the input. Without one, an
* error will result in an Exception being thrown.</p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
*/
- public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
+ public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
mOnPartialImageListener = l;
- return this;
}
/**
@@ -972,19 +1120,20 @@
* Crop the output to {@code subset} of the (possibly) scaled image.
*
* <p>{@code subset} must be contained within the size set by
- * {@link #setResize} or the bounds of the image if setResize was not
- * called. Otherwise an {@link IllegalStateException} will be thrown by
+ * {@link #setTargetSize} or the bounds of the image if setTargetSize was
+ * not called. Otherwise an {@link IllegalStateException} will be thrown by
* {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
*
* <p>NOT intended as a replacement for
* {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
* but merely crops the output.</p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ *
*/
- public ImageDecoder setCrop(@Nullable Rect subset) {
+ public void setCrop(@Nullable Rect subset) {
mCropRect = subset;
- return this;
}
/**
@@ -1001,13 +1150,13 @@
* If the image is a nine patch, this Rect will be set to the padding
* rectangle during decode. Otherwise it will not be modified.
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*
* @hide
*/
- public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+ public void setOutPaddingRect(@NonNull Rect outPadding) {
mOutPaddingRect = outPadding;
- return this;
}
/**
@@ -1026,20 +1175,37 @@
* order to modify. Attempting to decode a mutable {@link Drawable} will
* throw an {@link java.lang.IllegalStateException}.</p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*/
- public ImageDecoder setMutable(boolean mutable) {
+ public void setMutableRequired(boolean mutable) {
mMutable = mutable;
+ }
+
+ /** @removed
+ * @deprecated Renamed to {@link #setMutableRequired}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setMutable(boolean mutable) {
+ this.setMutableRequired(mutable);
return this;
}
/**
* Return whether the {@link Bitmap} will be mutable.
*/
- public boolean getMutable() {
+ public boolean isMutableRequired() {
return mMutable;
}
+ /** @removed
+ * @deprecated Renamed to {@link #isMutableRequired}.
+ */
+ @java.lang.Deprecated
+ public boolean getMutable() {
+ return this.isMutableRequired();
+ }
+
/**
* Specify whether to potentially save RAM at the expense of quality.
*
@@ -1052,11 +1218,11 @@
* This necessarily lowers the quality of the output, but saves half
* the memory used.</p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*/
- public ImageDecoder setConserveMemory(boolean conserveMemory) {
+ public void setConserveMemory(boolean conserveMemory) {
mConserveMemory = conserveMemory;
- return this;
}
/**
@@ -1077,45 +1243,97 @@
* with only one channel, treat that channel as alpha. Otherwise this call has
* no effect.</p>
*
- * <p>setDecodeAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
+ * <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
* combine them will result in {@link #decodeDrawable}/
* {@link #decodeBitmap} throwing an
* {@link java.lang.IllegalStateException}.</p>
*
- * @return this object for chaining.
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
*/
- public ImageDecoder setDecodeAsAlphaMask(boolean decodeAsAlphaMask) {
- mDecodeAsAlphaMask = decodeAsAlphaMask;
+ public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
+ mDecodeAsAlphaMask = enabled;
+ }
+
+ /** @removed
+ * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
+ */
+ @java.lang.Deprecated
+ public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
+ this.setDecodeAsAlphaMaskEnabled(enabled);
return this;
}
/** @removed
- * @deprecated Call {@link #setDecodeAsAlphaMask} instead.
+ * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
*/
@java.lang.Deprecated
public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
- return this.setDecodeAsAlphaMask(asAlphaMask);
+ this.setDecodeAsAlphaMask(asAlphaMask);
+ return this;
}
/**
* Return whether to treat single channel input as alpha.
*
- * <p>This returns whether {@link #setDecodeAsAlphaMask} was set to {@code true}.
- * It may still return {@code true} even if the image has more than one
- * channel and therefore will not be treated as an alpha mask.</p>
+ * <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
+ * {@code true}. It may still return {@code true} even if the image has
+ * more than one channel and therefore will not be treated as an alpha
+ * mask.</p>
*/
+ public boolean isDecodeAsAlphaMaskEnabled() {
+ return mDecodeAsAlphaMask;
+ }
+
+ /** @removed
+ * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
+ */
+ @java.lang.Deprecated
public boolean getDecodeAsAlphaMask() {
return mDecodeAsAlphaMask;
}
/** @removed
- * @deprecated Call {@link #getDecodeAsAlphaMask} instead.
+ * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
*/
@java.lang.Deprecated
public boolean getAsAlphaMask() {
return this.getDecodeAsAlphaMask();
}
+ /**
+ * Specify the desired {@link ColorSpace} for the output.
+ *
+ * <p>If non-null, the decoder will try to decode into this
+ * color space. If it is null, which is the default, or the request cannot
+ * be met, the decoder will pick either the color space embedded in the
+ * image or the color space best suited for the requested image
+ * configuration (for instance {@link ColorSpace.Named#SRGB sRGB} for
+ * the {@link Bitmap.Config#ARGB_8888} configuration).</p>
+ *
+ * <p>{@link Bitmap.Config#RGBA_F16} always uses the
+ * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space).
+ * Bitmaps in other configurations without an embedded color space are
+ * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+ *
+ * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
+ * currently supported. An <code>IllegalArgumentException</code> will
+ * be thrown by the decode methods when setting a non-RGB color space
+ * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
+ *
+ * <p class="note">The specified color space's transfer function must be
+ * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
+ * <code>IllegalArgumentException</code> will be thrown by the decode methods
+ * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
+ * specified color space returns null.</p>
+ *
+ * <p>Like all setters on ImageDecoder, this must be called inside
+ * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+ */
+ public void setTargetColorSpace(ColorSpace colorSpace) {
+ mDesiredColorSpace = colorSpace;
+ }
+
@Override
public void close() {
mCloseGuard.close();
@@ -1151,9 +1369,20 @@
}
}
- if (mPostProcessor != null && mRequireUnpremultiplied) {
+ if (mPostProcessor != null && mUnpremultipliedRequired) {
throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
}
+
+ if (mDesiredColorSpace != null) {
+ if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
+ throw new IllegalArgumentException("The target color space must use the "
+ + "RGB color model - provided: " + mDesiredColorSpace);
+ }
+ if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
+ throw new IllegalArgumentException("The target color space must use an "
+ + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
+ }
+ }
}
private static void checkSubset(int width, int height, Rect r) {
@@ -1166,13 +1395,14 @@
}
}
+ @WorkerThread
@NonNull
private Bitmap decodeBitmapInternal() throws IOException {
checkState();
return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
mDesiredWidth, mDesiredHeight, mCropRect,
- mMutable, mAllocator, mRequireUnpremultiplied,
- mConserveMemory, mDecodeAsAlphaMask);
+ mMutable, mAllocator, mUnpremultipliedRequired,
+ mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -1194,10 +1424,12 @@
* @param listener for learning the {@link ImageInfo} and changing any
* default settings on the {@code ImageDecoder}. This will be called on
* the same thread as {@code decodeDrawable} before that method returns.
+ * This is required in order to change any of the default settings.
* @return Drawable for displaying the image.
* @throws IOException if {@code src} is not found, is an unsupported
* format, or cannot be decoded for any reason.
*/
+ @WorkerThread
@NonNull
public static Drawable decodeDrawable(@NonNull Source src,
@NonNull OnHeaderDecodedListener listener) throws IOException {
@@ -1208,6 +1440,7 @@
return decodeDrawableImpl(src, listener);
}
+ @WorkerThread
@NonNull
private static Drawable decodeDrawableImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
@@ -1215,7 +1448,7 @@
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
- if (decoder.mRequireUnpremultiplied) {
+ if (decoder.mUnpremultipliedRequired) {
// Though this could be supported (ignored) for opaque images,
// it seems better to always report this error.
throw new IllegalStateException("Cannot decode a Drawable " +
@@ -1268,8 +1501,18 @@
}
/**
- * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}.
+ * Create a {@link Drawable} from a {@code Source}.
+ *
+ * <p>Since there is no {@link OnHeaderDecodedListener}, the default
+ * settings will be used. In order to change any settings, call
+ * {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
+ *
+ * @param src representing the encoded image.
+ * @return Drawable for displaying the image.
+ * @throws IOException if {@code src} is not found, is an unsupported
+ * format, or cannot be decoded for any reason.
*/
+ @WorkerThread
@NonNull
public static Drawable decodeDrawable(@NonNull Source src)
throws IOException {
@@ -1283,10 +1526,12 @@
* @param listener for learning the {@link ImageInfo} and changing any
* default settings on the {@code ImageDecoder}. This will be called on
* the same thread as {@code decodeBitmap} before that method returns.
+ * This is required in order to change any of the default settings.
* @return Bitmap containing the image.
* @throws IOException if {@code src} is not found, is an unsupported
* format, or cannot be decoded for any reason.
*/
+ @WorkerThread
@NonNull
public static Bitmap decodeBitmap(@NonNull Source src,
@NonNull OnHeaderDecodedListener listener) throws IOException {
@@ -1297,6 +1542,7 @@
return decodeBitmapImpl(src, listener);
}
+ @WorkerThread
@NonNull
private static Bitmap decodeBitmapImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
@@ -1353,7 +1599,7 @@
float scale = (float) dstDensity / srcDensity;
int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
- decoder.setResize(scaledWidth, scaledHeight);
+ decoder.setTargetSize(scaledWidth, scaledHeight);
return dstDensity;
}
@@ -1362,9 +1608,24 @@
return nGetMimeType(mNativePtr);
}
+ @Nullable
+ private ColorSpace getColorSpace() {
+ return nGetColorSpace(mNativePtr);
+ }
+
/**
- * See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
+ * Create a {@link Bitmap} from a {@code Source}.
+ *
+ * <p>Since there is no {@link OnHeaderDecodedListener}, the default
+ * settings will be used. In order to change any settings, call
+ * {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
+ *
+ * @param src representing the encoded image.
+ * @return Bitmap containing the image.
+ * @throws IOException if {@code src} is not found, is an unsupported
+ * format, or cannot be decoded for any reason.
*/
+ @WorkerThread
@NonNull
public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
return decodeBitmapImpl(src, null);
@@ -1410,12 +1671,14 @@
boolean doPostProcess,
int width, int height,
@Nullable Rect cropRect, boolean mutable,
- int allocator, boolean requireUnpremul,
- boolean conserveMemory, boolean decodeAsAlphaMask)
+ int allocator, boolean unpremulRequired,
+ boolean conserveMemory, boolean decodeAsAlphaMask,
+ @Nullable ColorSpace desiredColorSpace)
throws IOException;
private static native Size nGetSampledSize(long nativePtr,
int sampleSize);
private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
private static native void nClose(long nativePtr);
private static native String nGetMimeType(long nativePtr);
+ private static native ColorSpace nGetColorSpace(long nativePtr);
}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 098cdc6..cd0862c 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -24,6 +24,8 @@
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import libcore.util.NativeAllocationRegistry;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -32,10 +34,14 @@
* text on a path.
*/
public class Path {
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ Path.class.getClassLoader(), nGetFinalizer(), 48 /* dummy size */);
+
/**
* @hide
*/
- public long mNativePath;
+ public final long mNativePath;
/**
* @hide
@@ -52,6 +58,7 @@
*/
public Path() {
mNativePath = nInit();
+ sRegistry.registerNativeAllocation(this, mNativePath);
}
/**
@@ -69,6 +76,7 @@
}
}
mNativePath = nInit(valNative);
+ sRegistry.registerNativeAllocation(this, mNativePath);
}
/**
@@ -297,7 +305,7 @@
* a rectangle
* @return true if the path specifies a rectangle
*/
- public boolean isRect(RectF rect) {
+ public boolean isRect(@Nullable RectF rect) {
return nIsRect(mNativePath, rect);
}
@@ -771,15 +779,6 @@
nTransform(mNativePath, matrix.native_instance);
}
- protected void finalize() throws Throwable {
- try {
- nFinalize(mNativePath);
- mNativePath = 0; // Other finalizers can still call us.
- } finally {
- super.finalize();
- }
- }
-
/** @hide */
public final long readOnlyNI() {
return mNativePath;
@@ -820,7 +819,7 @@
private static native long nInit();
private static native long nInit(long nPath);
- private static native void nFinalize(long nPath);
+ private static native long nGetFinalizer();
private static native void nSet(long native_dst, long nSrc);
private static native void nComputeBounds(long nPath, RectF bounds);
private static native void nIncReserve(long nPath, int extraPtCount);
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 5a8fa07..0d32075 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -33,8 +33,8 @@
boolean isUserSelectable(String alias);
void setUserSelectable(String alias, boolean isUserSelectable);
- boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
- boolean attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
+ int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+ int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
out KeymasterCertificateChain chain);
boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 2daf733..46a7fa8 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -246,6 +246,82 @@
public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
/**
+ * Indicates that a call to {@link #generateKeyPair} was successful.
+ * @hide
+ */
+ public static final int KEY_GEN_SUCCESS = 0;
+
+ /**
+ * An alias was missing from the key specifications when calling {@link #generateKeyPair}.
+ * @hide
+ */
+ public static final int KEY_GEN_MISSING_ALIAS = 1;
+
+ /**
+ * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't
+ * have been provided.
+ * @hide
+ */
+ public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2;
+
+ /**
+ * Algorithm not supported by {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3;
+
+ /**
+ * Invalid algorithm parameters when calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4;
+
+ /**
+ * Keystore is not available when calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
+
+ /**
+ * General failure while calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_FAILURE = 6;
+
+ /**
+ * Successful call to {@link #attestKey}
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_SUCCESS = 0;
+
+ /**
+ * Attestation challenge missing when calling {@link #attestKey}
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1;
+
+ /**
+ * The caller requested Device ID attestation when calling {@link #attestKey}, but has no
+ * permissions to get device identifiers.
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2;
+
+ /**
+ * The underlying hardware does not support Device ID attestation or cannot attest to the
+ * identifiers that are stored on the device. This indicates permanent inability
+ * to get attestation records on the device.
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3;
+
+ /**
+ * General failure when calling {@link #attestKey}
+ * @hide
+ */
+ public static final int KEY_ATTESTATION_FAILURE = 4;
+
+ /**
* Returns an {@code Intent} that can be used for credential
* installation. The intent may be used without any extras, in
* which case the user will be able to install credentials from
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1924bbe..81644eb 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;
@@ -66,6 +67,7 @@
public static final int VALUE_CORRUPTED = 8;
public static final int UNDEFINED_ACTION = 9;
public static final int WRONG_PASSWORD = 10;
+ public static final int CANNOT_ATTEST_IDS = -66;
public static final int HARDWARE_TYPE_UNAVAILABLE = -68;
/**
@@ -278,7 +280,7 @@
/**
* 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) {
@@ -299,7 +301,7 @@
* 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.
*
@@ -545,6 +547,9 @@
try {
args = args != null ? args : new KeymasterArguments();
entropy = entropy != null ? entropy : new byte[0];
+ if (!args.containsTag(KeymasterDefs.KM_TAG_USER_ID)) {
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser());
+ }
return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 4b9f3c80..c342acd 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -266,6 +266,7 @@
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 @@
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 @@
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mIsStrongBoxBacked = isStrongBoxBacked;
mUserConfirmationRequired = userConfirmationRequired;
+ mUnlockedDeviceRequired = unlockedDeviceRequired;
}
/**
@@ -670,6 +673,15 @@
}
/**
+ * 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 @@
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 @@
}
/**
+ * 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 @@
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 95eeec7..22568ce 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -224,12 +224,13 @@
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 @@
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 @@
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 @@
* been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
*/
public boolean isTrustedUserPresenceRequired() {
- return mTrustedUserPresenceRequred;
+ return mTrustedUserPresenceRequired;
}
/**
@@ -505,6 +508,15 @@
}
/**
+ * 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 @@
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 @@
}
/**
+ * 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 @@
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 0ef08f2..6e50121 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,9 @@
* 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) {
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser());
+
if (spec.isUserConfirmationRequired()) {
args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
}
@@ -111,6 +111,10 @@
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 1949592..ad18ff8 100644
--- a/keystore/java/android/security/keystore/UserAuthArgs.java
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -33,5 +33,6 @@
boolean isUserConfirmationRequired();
long getBoundToSpecificSecureUserId();
boolean isTrustedUserPresenceRequired();
+ boolean isUnlockedDeviceRequired();
}
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index d9584db..293e45f 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -34,7 +34,8 @@
}
}
-bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
+bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
+ const SkRect* dstRect) {
if (context == nullptr) {
SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
return false;
@@ -43,8 +44,8 @@
SkMatrix layerTransform;
layer->getTransform().copyTo(layerTransform);
sk_sp<SkImage> layerImage;
- int layerWidth = layer->getWidth();
- int layerHeight = layer->getHeight();
+ const int layerWidth = layer->getWidth();
+ const int layerHeight = layer->getHeight();
if (layer->getApi() == Layer::Api::OpenGL) {
GlLayer* glLayer = static_cast<GlLayer*>(layer);
GrGLTextureInfo externalTexture;
@@ -62,21 +63,21 @@
}
if (layerImage) {
- SkMatrix textureMatrix;
- layer->getTexTransform().copyTo(textureMatrix);
+ SkMatrix textureMatrixInv;
+ layer->getTexTransform().copyTo(textureMatrixInv);
// TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
// use bottom left origin and remove flipV and invert transformations.
SkMatrix flipV;
flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
- textureMatrix.preConcat(flipV);
- textureMatrix.preScale(1.0f / layerWidth, 1.0f / layerHeight);
- textureMatrix.postScale(layerWidth, layerHeight);
- SkMatrix textureMatrixInv;
- if (!textureMatrix.invert(&textureMatrixInv)) {
- textureMatrixInv = textureMatrix;
+ textureMatrixInv.preConcat(flipV);
+ textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
+ textureMatrixInv.postScale(layerWidth, layerHeight);
+ SkMatrix textureMatrix;
+ if (!textureMatrixInv.invert(&textureMatrix)) {
+ textureMatrix = textureMatrixInv;
}
- SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
+ SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrix);
SkPaint paint;
paint.setAlpha(layer->getAlpha());
@@ -88,7 +89,20 @@
canvas->save();
canvas->concat(matrix);
}
- canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ if (dstRect) {
+ SkMatrix matrixInv;
+ if (!matrix.invert(&matrixInv)) {
+ matrixInv = matrix;
+ }
+ SkRect srcRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ matrixInv.mapRect(&srcRect);
+ SkRect skiaDestRect = *dstRect;
+ matrixInv.mapRect(&skiaDestRect);
+ canvas->drawImageRect(layerImage.get(), srcRect, skiaDestRect, &paint,
+ SkCanvas::kFast_SrcRectConstraint);
+ } else {
+ canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ }
// restore the original matrix
if (nonIdentityMatrix) {
canvas->restore();
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 3450387..18d1184 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,7 +32,8 @@
public:
explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
- static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer);
+ static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
+ const SkRect* dstRect = nullptr);
protected:
virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 2fa56f6..6700748 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -83,10 +83,12 @@
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/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 365d740..74cfb28 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -138,7 +138,9 @@
SkBudgeted::kYes, bitmap->info());
Layer* layer = deferredLayer->backingLayer();
- if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer)) {
+ const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
+ if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer,
+ &dstRect)) {
sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot();
if (tmpImage->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
bitmap->notifyPixelsChanged();
diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto
index f29f57f..1ef36df 100644
--- a/libs/incident/proto/android/privacy.proto
+++ b/libs/incident/proto/android/privacy.proto
@@ -16,13 +16,13 @@
syntax = "proto2";
-option java_package = "android";
+package android;
+
+option java_package = "com.android.incident";
option java_multiple_files = true;
import "google/protobuf/descriptor.proto";
-package android;
-
enum Destination {
// Fields or messages annotated with DEST_LOCAL must never be
// extracted from the device automatically. They can be accessed
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 86dfc9c..ca895fc 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -226,11 +226,10 @@
}
/**
- * @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 e408a11..aeef215 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4596,8 +4596,7 @@
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 8d62cac..5173743 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -705,7 +705,9 @@
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 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/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index befbade..aef31b1 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -4099,8 +4099,8 @@
/** The player was started because it was used as the next player for another
* player, which just completed playback.
+ * @see android.media.MediaPlayer#setNextMediaPlayer(MediaPlayer)
* @see android.media.MediaPlayer.OnInfoListener
- * @hide
*/
public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 004efea..d6399a4 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 @@
}
/**
- * @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/packages/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index 6504af1..e873157 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -8,5 +8,6 @@
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
LOCAL_CERTIFICATE := media
+LOCAL_COMPATIBILITY_SUITE += device-tests
include $(BUILD_PACKAGE)
diff --git a/packages/MtpDocumentsProvider/perf_tests/AndroidTest.xml b/packages/MtpDocumentsProvider/perf_tests/AndroidTest.xml
new file mode 100644
index 0000000..8b7292b
--- /dev/null
+++ b/packages/MtpDocumentsProvider/perf_tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<configuration description="Runs MtpDocumentsProviderPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="MtpDocumentsProviderPerfTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.mtp.perftests" />
+ </test>
+</configuration>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 82bc6af8..6368607 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/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index ee12191..f9f80eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -163,6 +163,11 @@
return mService.getActiveDevice();
}
+ public boolean isAudioOn() {
+ if (mService == null) return false;
+ return mService.isAudioOn();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 28833a3..835ff07 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.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 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 @@
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/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 4cd23f9..9347674 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -50,7 +50,9 @@
}
};
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
- .clearCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ .clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager
.NetworkCallback() {
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 8f80527..a128b54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -213,6 +213,7 @@
mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
new file mode 100644
index 0000000..117f447
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -0,0 +1,60 @@
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class HeadsetProfileTest {
+
+ @Mock
+ private LocalBluetoothAdapter mAdapter;
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothHeadset mService;
+
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private HeadsetProfile mProfile;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = spy(RuntimeEnvironment.application);
+
+ doAnswer((invocation) -> {
+ mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+ return null;
+ }).when(mAdapter).getProfileProxy(any(Context.class), any(), eq(BluetoothProfile.HEADSET));
+
+ mProfile = new HeadsetProfile(context, mAdapter, mDeviceManager, mProfileManager);
+ mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mService);
+ }
+
+ @Test
+ public void bluetoothProfile_shouldReturnTheAudioStatusFromBlueToothHeadsetService() {
+ when(mService.isAudioOn()).thenReturn(true);
+ assertThat(mProfile.isAudioOn()).isTrue();
+
+ when(mService.isAudioOn()).thenReturn(false);
+ assertThat(mProfile.isAudioOn()).isFalse();
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a444ac8..f43e719 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1724,6 +1724,9 @@
dumpSetting(s, p,
Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS,
SecureSettingsProto.Backup.LOCAL_TRANSPORT_PARAMETERS);
+ dumpSetting(s, p,
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+ SecureSettingsProto.Backup.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
p.end(backupToken);
// Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index c8bcdaa..5f4cf03 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -19,6 +19,7 @@
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Handler;
+import android.os.VibrationEffect;
import android.util.SparseArray;
import com.android.systemui.plugins.VolumeDialogController.Callbacks;
@@ -44,7 +45,8 @@
void setRingerMode(int ringerModeNormal, boolean external);
boolean hasVibrator();
- void vibrate();
+ void vibrate(VibrationEffect effect);
+ void scheduleTouchFeedback();
AudioManager getAudioManager();
diff --git a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
index 726d814..c0e0e59 100644
--- a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="17dp"
+ android:viewportWidth="14"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M5.62,16.29H4.58V9.06l-1.79,0.8V8.88l2.67-1.17h0.16V16.29z" />
+ android:pathData="M3.77,13.48H2.55V5.05L0.46,5.98V4.84l3.12-1.36h0.19V13.48z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M11.08,11.02l1.61-3.27h1.26l-2.22,4.23l2.27,4.3h-1.27l-1.64-3.33l-1.65,3.33H8.16l2.28-4.3L8.21,7.75h1.25L11.08,11.02z" />
+ android:pathData="M10.14,7.34l1.87-3.81h1.47L10.9,8.46l2.65,5.02h-1.48l-1.91-3.88l-1.92,3.88H6.74L9.4,8.46l-2.6-4.94h1.46L10.14,7.34z" />
<path
- android:pathData="M 0 0 H 13.99 V 24 H 0 V 0 Z" />
-</vector>
\ No newline at end of file
+ android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
index 7a539ff..e4a5bf8 100644
--- a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="17dp"
+ android:viewportWidth="14"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M3.83,11.38h0.66c0.43,0,0.75-0.13,0.98-0.39s0.35-0.62,0.35-1.07c0-1-0.39-1.5-1.16-1.5c-0.37,0-0.66,0.13-0.87,0.4 S3.47,9.44,3.47,9.88H2.44c0-0.68,0.21-1.25,0.62-1.69s0.95-0.67,1.6-0.67c0.67,0,1.21,0.21,1.6,0.63s0.59,1.01,0.59,1.78 c0,0.39-0.1,0.76-0.31,1.1s-0.47,0.59-0.8,0.75c0.8,0.3,1.21,0.96,1.21,2c0,0.76-0.21,1.37-0.64,1.82s-0.98,0.68-1.66,0.68 c-0.68,0-1.22-0.21-1.64-0.64s-0.62-1-0.62-1.73h1.04c0,0.45,0.11,0.81,0.33,1.08s0.52,0.4,0.9,0.4c0.39,0,0.69-0.13,0.92-0.39 s0.34-0.66,0.34-1.2c0-1.04-0.49-1.55-1.47-1.55H3.83V11.38z" />
+ android:pathData="M1.9,7.88h0.77c0.5,0,0.88-0.15,1.15-0.46s0.4-0.72,0.4-1.25c0-1.17-0.45-1.75-1.35-1.75c-0.43,0-0.77,0.16-1.02,0.47 S1.49,5.62,1.49,6.13h-1.2c0-0.8,0.24-1.46,0.73-1.97s1.11-0.78,1.86-0.78c0.78,0,1.41,0.25,1.87,0.73S5.43,5.31,5.43,6.2 c0,0.46-0.12,0.89-0.36,1.29S4.52,8.18,4.14,8.37c0.94,0.35,1.41,1.12,1.41,2.33c0,0.89-0.25,1.6-0.74,2.12 c-0.49,0.53-1.14,0.79-1.94,0.79c-0.79,0-1.43-0.25-1.91-0.75c-0.49-0.5-0.73-1.17-0.73-2.01h1.21c0,0.53,0.13,0.95,0.38,1.26 c0.26,0.31,0.6,0.47,1.05,0.47c0.45,0,0.81-0.15,1.08-0.46s0.4-0.77,0.4-1.39c0-1.21-0.57-1.81-1.72-1.81H1.9V7.88z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M14,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.15-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92H14V15.11z" />
+ android:pathData="M13.77,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26h-1.25 C12.47,5.82,12.3,5.33,12,4.98s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.59,6.37,8.58,7.4v2.03c0,1,0.19,1.77,0.57,2.31 c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59H10.7V8.52h3.07V12.24z" />
<path
- android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
-</vector>
\ No newline at end of file
+ android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
index b2fab0c8..e98560b 100644
--- a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="17dp"
+ android:viewportWidth="14"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6.42,13.3h0.95v0.88H6.42v1.98H5.38v-1.98H2.16v-0.64l3.17-5.91h1.09C6.42,7.63,6.42,13.3,6.42,13.3z M3.31,13.3h2.07 V9.25L3.31,13.3z" />
+ android:pathData="M5.07,10.13h1.11v1.03H5.07v2.31H3.86v-2.31H0.1v-0.75l3.7-6.9h1.27V10.13z M1.44,10.13h2.42V5.4L1.44,10.13z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M13.99,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.62V15.11z" />
+ android:pathData="M13.9,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.72,6.37,8.71,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
<path
- android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
index bdbb2df3..bf39ea2 100644
--- a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
@@ -14,20 +14,20 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="24dp"
- android:viewportWidth="20"
- android:viewportHeight="24">
+ android:width="22dp"
+ android:height="17dp"
+ android:viewportWidth="22"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6.18,13.3h0.95v0.88H6.18v1.98H5.14v-1.98H1.92v-0.64l3.17-5.91h1.09V13.3z M3.07,13.3h2.07V9.25L3.07,13.3z" />
+ android:pathData="M5.32,10.13h1.11v1.03H5.32v2.31H4.11v-2.31H0.35v-0.75l3.7-6.9h1.27V10.13z M1.69,10.13h2.42V5.4L1.69,10.13z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M13.75,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.63V15.11z" />
+ android:pathData="M14.15,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H12.9 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.97,6.37,8.96,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M 20 9.64 L 18 9.64 L 18 7.64 L 17 7.64 L 17 9.64 L 15 9.64 L 15 10.64 L 17 10.64 L 17 12.64 L 18 12.64 L 18 10.64 L 20 10.64 Z" />
+ android:pathData="M 19.3 5.74 L 19.3 3.39 L 18 3.39 L 18 5.74 L 15.65 5.74 L 15.65 7.04 L 18 7.04 L 18 9.39 L 19.3 9.39 L 19.3 7.04 L 21.65 7.04 L 21.65 5.74 Z" />
<path
- android:pathData="M 0 0 H 20 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 22 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
index 1a4a2e3..ca601d6 100644
--- a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="7dp"
- android:height="24dp"
- android:viewportWidth="7"
- android:viewportHeight="24">
+ android:width="6dp"
+ android:height="17dp"
+ android:viewportWidth="6"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6.5,12.32H3.48v3.02H7v0.92H2.41V7.73h4.53v0.92H3.48v2.75H6.5V12.32z" />
+ android:pathData="M5.1,8.88H1.57v3.53h4.1v1.07H0.32V3.52h5.28V4.6H1.57V7.8H5.1V8.88z" />
<path
- android:pathData="M 0 0 H 7 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 6 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
index d6a0488..8ff6d7a 100644
--- a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8dp"
- android:height="24dp"
- android:viewportWidth="8"
- android:viewportHeight="24">
+ android:width="7dp"
+ android:height="17dp"
+ android:viewportWidth="7"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M8,15.21l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.2 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68S7.95,9.39,8,10.23H6.93C6.88,9.7,6.74,9.28,6.49,8.99 S5.85,8.54,5.34,8.54c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69 c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96H5.37v-0.92H8V15.21z" />
+ android:pathData="M6.73,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13S2.5,3.39,3.62,3.39c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H5.48 c-0.05-0.62-0.22-1.1-0.52-1.45S4.22,4.46,3.62,4.46c-0.72,0-1.24,0.23-1.57,0.7S1.54,6.37,1.53,7.4v2.03c0,1,0.19,1.77,0.57,2.31 c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59H3.66V8.52h3.07V12.24z" />
<path
- android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 7 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
index be85bbb..68ea58e 100644
--- a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8dp"
- android:height="24dp"
- android:viewportWidth="8"
- android:viewportHeight="24">
+ android:width="7dp"
+ android:height="17dp"
+ android:viewportWidth="7"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M8,16.27H6.92v-3.94H3.49v3.94H2.42V7.73h1.07v3.67h3.43V7.73H8V16.27z" />
+ android:pathData="M6.76,13.48H5.5v-4.6H1.49v4.6H0.24V3.52h1.25V7.8H5.5V3.52h1.26V13.48z" />
<path
- android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 7 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
index f31f83c..42128002 100644
--- a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
@@ -14,17 +14,17 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="24dp"
- android:viewportWidth="14"
- android:viewportHeight="24">
+ android:width="15dp"
+ android:height="17dp"
+ android:viewportWidth="15"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M7.64,16.27H6.56v-3.94H3.13v3.94H2.06V7.73h1.07v3.67h3.43V7.73h1.08V16.27z" />
+ android:pathData="M7.01,13.48H5.75v-4.6H1.74v4.6H0.49V3.52h1.25V7.8h4.01V3.52h1.26V13.48z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M 14 9.73 L 12 9.73 L 12 7.73 L 11 7.73 L 11 9.73 L 9 9.73 L 9 10.73 L 11 10.73 L 11 12.73 L 12 12.73 L 12 10.73 L 14 10.73 Z" />
+ android:pathData="M 12.16 5.74 L 12.16 3.39 L 10.86 3.39 L 10.86 5.74 L 8.51 5.74 L 8.51 7.04 L 10.86 7.04 L 10.86 9.39 L 12.16 9.39 L 12.16 7.04 L 14.51 7.04 L 14.51 5.74 Z" />
<path
- android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 15 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
index e45b5e0..7536f51 100644
--- a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
@@ -14,20 +14,20 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="18dp"
- android:height="24dp"
- android:viewportWidth="18"
- android:viewportHeight="24">
+ android:width="18dp"
+ android:height="17dp"
+ android:viewportWidth="18"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M3.79,15.35h3.35v0.92H2.71V7.73h1.08V15.35z" />
+ android:pathData="M1.34,12.4h3.9v1.07H0.08V3.52h1.26V12.4z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M12.15,8.65H9.91v7.61H8.84V8.65H6.6V7.73h5.55V8.65z" />
+ android:pathData="M11.1,4.6H8.48v8.88H7.23V4.6H4.62V3.52h6.48V4.6z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M17.5,12.32h-3.02v3.02H18v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+ android:pathData="M17.34,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
<path
- android:pathData="M 0 0 H 18 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 18 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
index 553a5bd..302e3bd 100644
--- a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
@@ -14,23 +14,23 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="26dp"
+ android:height="17dp"
+ android:viewportWidth="26"
+ android:viewportHeight="17">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M3.91,15.35h3.35v0.92H2.84V7.73h1.08V15.35z" />
+ android:pathData="M1.59,12.4h3.9v1.07H0.33V3.52h1.26V12.4z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M12.28,8.65h-2.24v7.61H8.96V8.65H6.73V7.73h5.55V8.65z" />
+ android:pathData="M11.35,4.6H8.73v8.88H7.48V4.6H4.87V3.52h6.48V4.6z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M17.63,12.32h-3.02v3.02h3.52v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+ android:pathData="M17.59,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M 24 9.76 L 22 9.76 L 22 7.76 L 21 7.76 L 21 9.76 L 19 9.76 L 19 10.76 L 21 10.76 L 21 12.76 L 22 12.76 L 22 10.76 L 24 10.76 Z" />
+ android:pathData="M 23.32 5.74 L 23.32 3.39 L 22.02 3.39 L 22.02 5.74 L 19.67 5.74 L 19.67 7.04 L 22.02 7.04 L 22.02 9.39 L 23.32 9.39 L 23.32 7.04 L 25.67 7.04 L 25.67 5.74 Z" />
<path
- android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ android:pathData="M 0 0 H 26 V 17 H 0 V 0 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/qs_header_status_dot.xml b/packages/SystemUI/res/drawable/qs_header_status_dot.xml
deleted file mode 100644
index 69bfd49..0000000
--- a/packages/SystemUI/res/drawable/qs_header_status_dot.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
- <solid android:color="@android:color/white"/>
-</shape>
diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
index c3e36f2..a4b3c99 100644
--- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
@@ -1,4 +1,18 @@
<?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.
+-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimaryDark" />
diff --git a/packages/SystemUI/res/drawable/rounded_ripple.xml b/packages/SystemUI/res/drawable/rounded_ripple.xml
new file mode 100644
index 0000000..5588eb2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/rounded_ripple.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#FFFFFFFF"/>
+ <corners android:radius="8dp"/>
+ </shape>
+ </item>
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="#FFFFFFFF"/>
+ <corners android:radius="8dp"/>
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
index bd2edf3..0dd9f5a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_roaming.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -1,28 +1,27 @@
<!--
-Copyright (C) 2014 The Android Open Source Project
+ 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
+ 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
+ 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.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/signal_icon_size"
android:height="@dimen/signal_icon_size"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M7.8,7.2L9,10H7L5.87,7.33H4V10H2V2h5c1.13,0,2,0.87,2,2v1.33C9,6.13,8.47,6.87,7.8,7.2z M7,4H4v1.33h3V4z" />
- <path
- android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
- <path
- android:pathData="M0,0h24v24H0V0z" />
-</vector>
\ No newline at end of file
+ android:viewportWidth="17"
+ android:viewportHeight="17">
+
+<path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M2.93,4.81H1.81V7.4H1V1h1.79c0.63,0,1.1,0.16,1.42,0.49S4.7,2.29,4.7,2.92c0,0.4-0.09,0.74-0.26,1.04 C4.26,4.25,4.02,4.48,3.7,4.63l1.24,2.72V7.4H4.07L2.93,4.81z M1.81,4.12h0.98c0.34,0,0.61-0.11,0.81-0.33 c0.2-0.22,0.3-0.51,0.3-0.87c0-0.82-0.37-1.23-1.12-1.23H1.81V4.12z" />
+<path
+ android:pathData="M 0 0 H 17 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
index e45c0f9..94cc001 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog.xml
@@ -38,7 +38,7 @@
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false"
- android:elevation="@dimen/volume_panel_elevation" >
+ android:elevation="@dimen/volume_dialog_elevation" >
<LinearLayout
android:id="@+id/car_volume_dialog_rows"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index cc6e3bf..5ecd380 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -50,7 +50,8 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:paddingEnd="1dp"
+ android:paddingStart="1dp"
+ android:paddingEnd="2dp"
android:visibility="gone" />
<Space
android:id="@+id/mobile_roaming_space"
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index cd3271f..c03f25c 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/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 03e8451..54baa4a 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -54,15 +54,14 @@
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
android:textAppearance="@style/TextAppearance.QS.TileLabel" />
- <ImageView
+ <View
android:id="@+id/status_separator"
- android:layout_width="2dp"
- android:layout_height="2dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/qs_header_status_dot"
- android:tint="?android:attr/textColorPrimary" />
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ android:background="@android:color/white"
+ android:backgroundTint="?android:attr/textColorPrimary" />
<ImageView
android:id="@+id/ringer_mode_icon"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index ca8fcba..ed18dc7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -22,6 +22,7 @@
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_total_height"
android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:background="@android:color/transparent"
android:baselineAligned="false"
android:clickable="false"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 388b633..f38129f 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -31,23 +31,25 @@
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:gravity="center_vertical|start"
+ android:singleLine="true"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- systemui:showDark="false" />
+ android:gravity="center_vertical|start"
+ systemui:showDark="false"
+ />
<com.android.systemui.statusbar.policy.DateView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
+ android:padding="4dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
android:textSize="@dimen/qs_time_collapsed_size"
+ android:gravity="center_vertical"
systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
<android.widget.Space
@@ -55,11 +57,12 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:gravity="center_vertical|center_horizontal" />
+ android:gravity="center_vertical|center_horizontal"
+ />
- <com.android.systemui.BatteryMeterView
- android:id="@+id/battery"
+ <com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
- android:gravity="center_vertical|end" />
+ android:gravity="center_vertical|end"
+ />
</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 0000000..d607c8c
--- /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 0000000..08cef55
--- /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 1fafb2f..258b82a 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/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 0a3f4eb..d4fe1c6 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -17,9 +17,11 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:padding="@dimen/volume_dialog_panel_transparent_padding"
android:background="@android:color/transparent"
android:theme="@style/qs_theme"
- android:clipChildren="false" >
+ android:clipChildren="false"
+ android:clipToPadding="false">
<!-- right-aligned to be physically near volume button -->
<LinearLayout
android:id="@+id/volume_dialog"
@@ -30,22 +32,25 @@
android:background="@android:color/transparent"
android:layout_margin="@dimen/volume_dialog_base_margin"
android:orientation="vertical"
- android:clipChildren="false" >
+ android:clipChildren="false"
+ android:clipToPadding="false" >
<FrameLayout
android:id="@+id/ringer"
android:layout_width="@dimen/volume_dialog_ringer_size"
android:layout_height="@dimen/volume_dialog_ringer_size"
android:layout_marginBottom="@dimen/volume_dialog_spacer"
- android:elevation="@dimen/volume_panel_elevation"
+ android:translationZ="@dimen/volume_dialog_elevation"
android:layout_gravity="right"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:background="@drawable/rounded_bg_full">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/ringer_icon"
style="@style/VolumeButtons"
- android:background="?android:selectableItemBackgroundBorderless"
- android:layout_width="@dimen/volume_dialog_tap_target_size"
- android:layout_height="@dimen/volume_dialog_tap_target_size"
+ android:background="@drawable/rounded_ripple"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:tint="@color/accent_tint_color_selector"
android:layout_gravity="center"
android:soundEffectsEnabled="false" />
@@ -59,10 +64,10 @@
android:minWidth="@dimen/volume_dialog_panel_width"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:translationZ="@dimen/volume_dialog_elevation"
android:clipChildren="false"
android:clipToPadding="false"
- android:background="@drawable/rounded_bg_full"
- android:elevation="@dimen/volume_panel_elevation" >
+ android:background="@drawable/rounded_bg_full" >
<LinearLayout
android:id="@+id/volume_dialog_rows"
android:layout_width="wrap_content"
@@ -84,7 +89,7 @@
android:layout_height="@dimen/volume_dialog_tap_target_size"
android:layout_gravity="center"
android:contentDescription="@string/accessibility_volume_settings"
- android:background="?android:selectableItemBackgroundBorderless"
+ android:background="@drawable/ripple_drawable_20dp"
android:tint="?android:attr/colorControlNormal"
android:soundEffectsEnabled="false" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index bcc3692..6128da8 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -60,7 +60,7 @@
style="@style/VolumeButtons"
android:layout_width="@dimen/volume_dialog_tap_target_size"
android:layout_height="@dimen/volume_dialog_tap_target_size"
- android:background="?android:selectableItemBackgroundBorderless"
+ android:background="@drawable/ripple_drawable_20dp"
android:layout_marginBottom="@dimen/volume_dialog_row_margin_bottom"
android:tint="@color/accent_tint_color_selector"
android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6873dea..84ca657 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -270,13 +270,15 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
+ <dimen name="volume_dialog_panel_transparent_padding">8dp</dimen>
+
<!-- the amount the volume panel should be offset at the end from the view next to it (or
the screen edge, in portrait-->
<dimen name="volume_dialog_base_margin">8dp</dimen>
<dimen name="volume_dialog_panel_width">64dp</dimen>
- <dimen name="volume_dialog_slider_height">108dp</dimen>
+ <dimen name="volume_dialog_slider_height">116dp</dimen>
<dimen name="volume_dialog_row_height">252dp</dimen>
@@ -286,7 +288,7 @@
<dimen name="volume_dialog_spacer">4dp</dimen>
- <dimen name="volume_dialog_slider_margin_top">22dp</dimen>
+ <dimen name="volume_dialog_slider_margin_top">14dp</dimen>
<dimen name="volume_dialog_slider_margin_bottom">-2dp</dimen>
@@ -294,6 +296,8 @@
<dimen name="volume_dialog_settings_icon_size">16dp</dimen>
+ <dimen name="volume_dialog_elevation">9dp</dimen>
+
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
@@ -736,8 +740,6 @@
<dimen name="volume_expander_margin_end">2dp</dimen>
<dimen name="volume_expander_margin_top">6dp</dimen>
- <dimen name="volume_panel_elevation">8dp</dimen>
-
<!-- Padding between icon and text for managed profile toast -->
<dimen name="managed_profile_toast_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index 5679dd2..6caed61 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 419e9d2..4074042 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1350,7 +1350,7 @@
<string name="volume_dialog_title">%s volume controls</string>
- <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring</string>
+ <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring (<xliff:g id="volume level" example="56">%1$s</xliff:g>)</string>
<string name="output_title">Media output</string>
<string name="output_calls_title">Phone call output</string>
@@ -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/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 0683514..1ae06d7 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -81,14 +81,6 @@
private float mDarkIntensity;
private int mUser;
- /**
- * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings.
- */
- private boolean mUseWallpaperTextColors;
-
- private int mNonAdaptedForegroundColor;
- private int mNonAdaptedBackgroundColor;
-
public BatteryMeterView(Context context) {
this(context, null, 0);
}
@@ -148,29 +140,6 @@
updateShowPercent();
}
- /**
- * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
- * revert back to dark-mode-based/tinted colors.
- *
- * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all
- * components
- */
- public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
- if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) {
- return;
- }
-
- mUseWallpaperTextColors = shouldUseWallpaperTextColor;
-
- if (mUseWallpaperTextColors) {
- updateColors(
- Utils.getColorAttr(mContext, R.attr.wallpaperTextColor),
- Utils.getColorAttr(mContext, R.attr.wallpaperTextColorSecondary));
- } else {
- updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
- }
- }
-
public void setColorsFromContext(Context context) {
if (context == null) {
return;
@@ -210,8 +179,7 @@
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
updateShowPercent();
- Dependency.get(TunerService.class)
- .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
Dependency.get(ConfigurationController.class).addCallback(this);
mUserTracker.startTracking();
}
@@ -305,23 +273,19 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
mDarkIntensity = darkIntensity;
-
float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
- mNonAdaptedForegroundColor = getColorForDarkIntensity(
- intensity, mLightModeFillColor, mDarkModeFillColor);
- mNonAdaptedBackgroundColor = getColorForDarkIntensity(
- intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor);
-
- if (!mUseWallpaperTextColors) {
- updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
- }
+ int foreground = getColorForDarkIntensity(intensity, mLightModeFillColor,
+ mDarkModeFillColor);
+ int background = getColorForDarkIntensity(intensity, mLightModeBackgroundColor,
+ mDarkModeBackgroundColor);
+ mDrawable.setColors(foreground, background);
+ setTextColor(foreground);
}
- private void updateColors(int foregroundColor, int backgroundColor) {
- mDrawable.setColors(foregroundColor, backgroundColor);
- mTextColor = foregroundColor;
+ public void setTextColor(int color) {
+ mTextColor = color;
if (mBatteryPercentView != null) {
- mBatteryPercentView.setTextColor(foregroundColor);
+ mBatteryPercentView.setTextColor(color);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 816c598..8cff56d 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -228,9 +228,14 @@
mHandler.removeCallbacks(mConnectionRunnable);
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
- boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
- mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
- UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+ boolean bound = false;
+ try {
+ bound = mContext.bindServiceAsUser(launcherServiceIntent,
+ mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+ } catch (SecurityException e) {
+ Log.e(TAG_OPS, "Unable to bind because of security error", e);
+ }
if (!bound) {
// Retry after exponential backoff timeout
final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 1a9655e..2a27147 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -53,7 +53,8 @@
Key.NUM_APPS_LAUNCHED,
Key.HAS_SEEN_RECENTS_ONBOARDING,
Key.SEEN_RINGER_GUIDANCE_COUNT,
- Key.QS_HAS_TURNED_OFF_MOBILE_DATA
+ Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
+ Key.TOUCHED_RINGER_TOGGLE
})
public @interface Key {
@Deprecated
@@ -91,6 +92,7 @@
String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
+ String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 2c0e95b..a61ce8c 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.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 @@
mServices[i].onBootCompleted();
}
}
+
+ IntentFilter localeChangedFilter = new IntentFilter(
+ Intent.ACTION_LOCALE_CHANGED);
+ registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
}
}, filter);
} else {
@@ -249,4 +239,14 @@
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 f08219a..c409f73 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 java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.time.Duration;
import java.util.Arrays;
public class PowerUI extends SystemUI {
@@ -61,6 +62,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
|| 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;
}
+ int warnLevel = mLowBatteryReminderLevels[0];
+ int critLevel = mLowBatteryReminderLevels[1];
- // Only show the time based warning once per charge cycle
- final boolean canShowWarning = timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
- && !mLowWarningShownThisChargeCycle;
+ // Only show the low warning once per charge cycle
+ final boolean canShowWarning = !mLowWarningShownThisChargeCycle
+ && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
+ || mBatteryLevel <= warnLevel);
- // Only show the severe time based warning once per charge cycle
- final boolean canShowSevereWarning =
- timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
- && !mSevereWarningShownThisChargeCycle;
+ // 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/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7161463..bfbfbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,15 +18,17 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.ExpandableOutlineView;
/**
* Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -42,11 +44,7 @@
protected float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mQSFooter;
-
private View mBackground;
- private View mBackgroundGradient;
- private View mStatusBarBackground;
-
private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -62,8 +60,6 @@
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mBackground = findViewById(R.id.quick_settings_background);
- mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background);
- mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view);
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
@@ -72,22 +68,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- // Hide the backgrounds when in landscape mode.
- if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mBackgroundGradient.setVisibility(View.INVISIBLE);
- mStatusBarBackground.setVisibility(View.INVISIBLE);
- } else {
- mBackgroundGradient.setVisibility(View.VISIBLE);
- mStatusBarBackground.setVisibility(View.VISIBLE);
- }
-
- updateResources();
- }
-
- @Override
public boolean performClick() {
// Want to receive clicks so missing QQS tiles doesn't cause collapse, but
// don't want to do anything with them.
@@ -121,14 +101,6 @@
updateExpansion();
}
- private void updateResources() {
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
- layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
-
- mQSPanel.setLayoutParams(layoutParams);
- }
-
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ca88d70..df65d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
@@ -48,7 +47,6 @@
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;
@@ -56,10 +54,8 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
-import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.NextAlarmController;
import java.util.Locale;
@@ -94,7 +90,6 @@
private TouchAnimator mStatusIconsAlphaAnimator;
private TouchAnimator mHeaderTextContainerAlphaAnimator;
- private View mSystemIconsView;
private View mQuickQsStatusIcons;
private View mDate;
private View mHeaderTextContainerView;
@@ -112,9 +107,6 @@
private View mStatusSeparator;
private ImageView mRingerModeIcon;
private TextView mRingerModeTextView;
- private BatteryMeterView mBatteryMeterView;
- private Clock mClockView;
- private DateView mDateView;
private NextAlarmController mAlarmController;
/** Counts how many times the long press tooltip has been shown to the user. */
@@ -146,7 +138,6 @@
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
mDate = findViewById(R.id.date);
mDate.setOnClickListener(this);
- mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
@@ -169,15 +160,12 @@
// 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);
- mBatteryMeterView = findViewById(R.id.battery);
- mBatteryMeterView.setForceShowPercent(true);
- mClockView = findViewById(R.id.clock);
- mDateView = findViewById(R.id.date);
+ BatteryMeterView battery = findViewById(R.id.battery);
+ battery.setForceShowPercent(true);
}
private void updateStatusText() {
@@ -224,13 +212,6 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateResources();
-
- // Update color schemes in landscape to use wallpaperTextColor
- boolean shouldUseWallpaperTextColor =
- newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
- mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
- mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
- mDateView.useWallpaperTextColor(shouldUseWallpaperTextColor);
}
@Override
@@ -240,21 +221,11 @@
}
private void updateResources() {
- Resources resources = mContext.getResources();
-
- // Update height for a few views, especially due to landscape mode restricting space.
+ // Update height, especially due to landscape mode restricting space.
mHeaderTextContainerView.getLayoutParams().height =
- resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
+ mContext.getResources().getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
- mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
- mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
-
- getLayoutParams().height =
- resources.getDimensionPixelSize(com.android.internal.R.dimen.quick_qs_total_height);
- setLayoutParams(getLayoutParams());
-
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
}
@@ -526,8 +497,9 @@
mHeaderQsPanel.setHost(host, null /* No customization in header */);
// Use SystemUI context to get battery meter colors, and let it use the default tint (white)
- mBatteryMeterView.setColorsFromContext(mHost.getContext());
- mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+ BatteryMeterView battery = findViewById(R.id.battery);
+ battery.setColorsFromContext(mHost.getContext());
+ battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
}
public void setCallback(Callback qsPanelCallback) {
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 ec18376..31c455d 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 @@
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/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 943a176..6f664d7 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 @@
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 a9defc8..09d928f 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 @@
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 @@
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/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 674ccd8..0f85c5b 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 @@
}
}
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 03b263d..3ece2f9 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.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.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.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 @@
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 @@
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 @@
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 0000000..cdb0514
--- /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 c9c1bc6..20e5f86 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 @@
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 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 @@
*/
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 @@
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/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 402d9fdd..4b6ab64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -18,25 +18,21 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import android.Manifest;
import android.app.AppGlobals;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.Context;
import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.SnoozeCriterion;
@@ -46,10 +42,8 @@
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
-import android.Manifest;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.Dependency;
@@ -454,47 +448,44 @@
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- public boolean shouldSuppressFullScreenIntent(String key) {
+ public boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ public boolean shouldSuppressPeek(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_PEEK);
+ }
+
+ public boolean shouldSuppressStatusBar(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ public boolean shouldSuppressAmbient(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ public boolean shouldSuppressNotificationList(StatusBarNotification sbn) {
+ return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
+
+ private boolean shouldSuppressVisualEffect(StatusBarNotification sbn, int effect) {
+ if (isExemptFromDndVisualSuppression(sbn)) {
+ return false;
+ }
+ String key = sbn.getKey();
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0;
+ return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
}
return false;
}
- public boolean shouldSuppressPeek(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_PEEK) != 0;
+ protected boolean isExemptFromDndVisualSuppression(StatusBarNotification sbn) {
+ if ((sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
}
- return false;
- }
-
- public boolean shouldSuppressStatusBar(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_STATUS_BAR) != 0;
- }
- return false;
- }
-
- public boolean shouldSuppressAmbient(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_AMBIENT) != 0;
- }
- return false;
- }
-
- public boolean shouldSuppressNotificationList(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return (mTmpRanking.getSuppressedVisualEffects()
- & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+ if (sbn.getNotification().isMediaNotification()) {
+ return true;
}
return false;
}
@@ -620,11 +611,11 @@
return true;
}
- if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn.getKey())) {
+ if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn)) {
return true;
}
- if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn.getKey())) {
+ if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7a7cc99..45df450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -299,12 +299,12 @@
updateNotifications();
}
- private boolean shouldSuppressFullScreenIntent(String key) {
+ private boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
if (mPresenter.isDeviceInVrMode()) {
return true;
}
- return mNotificationData.shouldSuppressFullScreenIntent(key);
+ return mNotificationData.shouldSuppressFullScreenIntent(sbn);
}
private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
@@ -690,7 +690,7 @@
NotificationData.Entry shadeEntry = createNotificationViews(notification);
boolean isHeadsUped = shouldPeek(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
- if (shouldSuppressFullScreenIntent(key)) {
+ if (shouldSuppressFullScreenIntent(notification)) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
}
@@ -846,13 +846,13 @@
return false;
}
- if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn.getKey())) {
+ if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn)) {
if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
return false;
}
// Peeking triggers an ambient display pulse, so disable peek is ambient is active
- if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn.getKey())) {
+ if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn)) {
if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 75204d9..dff5f38 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 @@
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 @@
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 @@
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 @@
}
/**
- * @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 82ad74e..6238526 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.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 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 @@
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 @@
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 @@
CheckSaveListener checkSaveListener,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
- Set<String> nonBlockablePkgs,
+ boolean isNonblockable,
boolean isForBlockingHelper,
boolean isUserSentimentNegative)
throws RemoteException {
@@ -162,6 +159,7 @@
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 @@
&& 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 @@
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 @@
}
private void saveImportance() {
- if (mNonblockable) {
+ if (mIsNonblockable) {
return;
}
MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
@@ -314,7 +296,7 @@
keep.setOnClickListener(mOnKeepShowing);
minimize.setOnClickListener(mOnStopMinNotifications);
- if (mNonblockable) {
+ if (mIsNonblockable) {
keep.setText(R.string.notification_done);
block.setVisibility(GONE);
minimize.setVisibility(GONE);
@@ -326,15 +308,15 @@
minimize.setVisibility(GONE);
}
- // Set up app settings link
+ // Set up app settings link (i.e. Customize)
TextView settingsLinkView = findViewById(R.id.app_settings);
Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
mSbn.getId(), mSbn.getTag());
- if (settingsIntent != null
+ if (!mIsForBlockingHelper
+ && settingsIntent != null
&& !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
settingsLinkView.setVisibility(VISIBLE);
- settingsLinkView.setText(mContext.getString(R.string.notification_app_settings,
- mSbn.getNotification().getSettingsText()));
+ settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
settingsLinkView.setOnClickListener((View view) -> {
mAppSettingsClickListener.onClick(view, settingsIntent);
});
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 0000000..56f78f4
--- /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 603902a..bd6bd12 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.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 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 @@
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 0000000..b7620f3
--- /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 0000000..afd373e
--- /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 0000000..ccab0d6
--- /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 2841136..b7d501e 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 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 a95d0a4..3530e0b 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.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 @@
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 @@
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 @@
mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
mRightNavigationBarView = null;
}
-
super.destroy();
}
+
@Override
protected void makeStatusBarView() {
super.makeStatusBarView();
@@ -167,129 +214,132 @@
@Override
protected void createNavigationBar() {
- mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
- if (mNavigationBarView != null) {
- return;
- }
-
mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
- if (mShowBottom) {
- buildBottomBar();
- }
-
- int widthForSides = mContext.getResources().getDimensionPixelSize(
- R.dimen.navigation_bar_height_car_mode);
-
-
mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
-
- if (mShowLeft) {
- buildLeft(widthForSides);
- }
-
mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+ buildNavBarWindows();
+ buildNavBarContent();
+ attachNavBarWindows();
+ }
+
+ private void buildNavBarContent() {
+ if (mShowBottom) {
+ buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+ R.layout.car_navigation_bar_unprovisioned);
+ }
+
+ if (mShowLeft) {
+ buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+ R.layout.car_left_navigation_bar_unprovisioned);
+ }
+
if (mShowRight) {
- buildRight(widthForSides);
+ buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+ R.layout.car_right_navigation_bar_unprovisioned);
+ }
+ }
+
+ private void buildNavBarWindows() {
+ if (mShowBottom) {
+
+ mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, null);
+ }
+ if (mShowLeft) {
+ mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, null);
+ }
+ if (mShowRight) {
+ mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, null);
}
}
+ private void attachNavBarWindows() {
- private void buildBottomBar() {
+ 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) {
+ 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(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 23bf887..7d283d9 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 @@
};
/**
+ * 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 f42473d..75b31c5 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 @@
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.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 @@
private int mDisabled1;
private StatusBar mStatusBarComponent;
private DarkIconManager mDarkIconManager;
- private SignalClusterView mSignalClusterView;
private View mOperatorNameFrame;
private SignalCallback mSignalCallback = new SignalCallback() {
@@ -99,9 +95,6 @@
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 @@
@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 3f9ae80..80c4eb0 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 @@
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 edfd02b..48540b11 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.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 void remove() {
+ mMobileViews.clear();
((ViewGroup) getParent()).removeView(this);
}
@@ -66,7 +75,7 @@
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 @@
}
}
+ /// 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 @@
}
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 @@
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/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index fcd4e8f..df2b817 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -334,6 +334,20 @@
return mKeyguardView != null && mKeyguardView.hasDismissActions();
}
+ public int getTop() {
+ if (mKeyguardView == null) {
+ return 0;
+ }
+
+ int top = mKeyguardView.getTop();
+ // The password view has an extra top padding that should be ignored.
+ if (mKeyguardView.getCurrentSecurityMode() == SecurityMode.Password) {
+ View messageArea = mKeyguardView.findViewById(R.id.keyguard_message_area);
+ top += messageArea.getTop();
+ }
+ return top;
+ }
+
protected void ensureView() {
// Removal of the view might be deferred to reduce unlock latency,
// in this case we need to force the removal, otherwise we'll
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 19e8295..3d7067d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -114,6 +114,11 @@
private boolean mTracking;
/**
+ * Distance in pixels between the top of the screen and the first view of the bouncer.
+ */
+ private int mBouncerTop;
+
+ /**
* Refreshes the dimension values.
*/
public void loadDimens(Resources res) {
@@ -129,7 +134,7 @@
public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
- float dark, boolean secure, boolean tracking) {
+ float dark, boolean secure, boolean tracking, int bouncerTop) {
mMinTopMargin = minTopMargin + mContainerTopPadding;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
@@ -140,6 +145,7 @@
mDarkAmount = dark;
mCurrentlySecure = secure;
mTracking = tracking;
+ mBouncerTop = bouncerTop;
}
public void run(Result result) {
@@ -189,8 +195,10 @@
private int getClockY() {
// Dark: Align the bottom edge of the clock at about half of the screen:
final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
- float clockYRegular = getExpandedClockPosition();
- float clockYTarget = mCurrentlySecure ? mMinTopMargin : -mKeyguardStatusHeight;
+ final float clockYRegular = getExpandedClockPosition();
+ final boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
+ float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
+ mMinTopMargin : -mKeyguardStatusHeight;
// Move clock up while collapsing the shade
float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
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 994c0ab..b817809 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 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
updateLayoutConsideringCutout();
- setSignalClusterLayoutWidth();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@@ -296,17 +295,6 @@
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 @@
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/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 9063dea..b6a11f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -150,7 +150,7 @@
// showAmbient == show in shade but not shelf
if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
- entry.key)) {
+ entry.notification)) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index cccda90..27ca0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -160,6 +160,7 @@
protected int mQsMinExpansionHeight;
protected int mQsMaxExpansionHeight;
private int mQsPeekHeight;
+ private int mBouncerTop;
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionFromOverscroll;
private float mLastOverscroll;
@@ -476,7 +477,8 @@
mKeyguardStatusView.getHeight(),
mDarkAmount,
mStatusBar.isKeyguardCurrentlySecure(),
- mTracking);
+ mTracking,
+ mBouncerTop);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY);
@@ -550,6 +552,11 @@
return count;
}
+ public void setBouncerTop(int bouncerTop) {
+ mBouncerTop = bouncerTop;
+ positionClockAndNotifications();
+ }
+
private void startClockAnimation(int x, int y) {
if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) {
return;
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 33c3ee9..3e7b0d9 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 @@
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 fb1addf..12bdfc6 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 @@
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 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 @@
anims.playTogether(
animateTransitionTo(mLeftSide, newAlpha),
animateTransitionTo(mStatusIcons, newAlpha),
- animateTransitionTo(mSignalCluster, newAlpha),
animateTransitionTo(mBattery, newAlphaBC),
animateTransitionTo(mClock, newAlphaBC)
);
@@ -102,7 +100,6 @@
} 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/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 4e12936..2c025b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -85,7 +85,7 @@
/**
* Default alpha value for most scrims.
*/
- public static final float GRADIENT_SCRIM_ALPHA = 0.70f;
+ public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
/**
* A scrim varies its opacity based on a busyness factor, for example
* how many notifications are currently visible.
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 c03ecb3..750d2a5 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 @@
protected boolean mBouncerShowing;
private PhoneStatusBarPolicy mIconPolicy;
+ private StatusBarSignalPolicy mSignalPolicy;
private VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
@@ -562,7 +563,7 @@
};
private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private UserSwitcherController mUserSwitcherController;
+ protected UserSwitcherController mUserSwitcherController;
private NetworkController mNetworkController;
private KeyguardMonitorImpl mKeyguardMonitor
= (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
@@ -588,7 +589,7 @@
}
};
private boolean mNoAnimationOnNextBarModeChange;
- private FalsingManager mFalsingManager;
+ protected FalsingManager mFalsingManager;
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -747,6 +748,7 @@
// 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 @@
}
protected void reevaluateStyles() {
- inflateSignalClusters();
inflateFooterView();
updateFooter();
inflateEmptyShadeView();
@@ -1145,36 +1146,6 @@
}
}
- 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;
@@ -3448,6 +3419,13 @@
return updateIsKeyguard();
}
+ /**
+ * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER.
+ */
+ public boolean isFullScreenUserSwitcherState() {
+ return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
+ }
+
private boolean updateIsKeyguard() {
boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
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 956bebb..94e004b 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 @@
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.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 @@
@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 @@
@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 @@
}
@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 @@
// 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 @@
}
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 @@
}
@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 @@
}
protected void onRemoveIcon(int viewIndex) {
+ if (mIsInDemoMode) {
+ mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
+ }
mGroup.removeViewAt(viewIndex);
}
@@ -264,17 +345,59 @@
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 8f5e705..510af03 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.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 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 @@
*/
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 @@
@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 @@
}
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(int index) {
- if (getIcon(index) == null) {
+ public void removeIcon(String slot, int tag) {
+ removeIcon(getSlotIndex(slot), tag);
+ }
+
+ @Override
+ public void removeAllIconsForSlot(String slotName) {
+ Slot slot = getSlot(slotName);
+ if (!slot.hasIconsInSlot()) {
return;
}
- mIconLogger.onIconHidden(getSlot(index));
- super.removeIcon(index);
- int viewIndex = getViewIndex(index);
+
+ 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 removeIcon(int index, int tag) {
+ if (getIcon(index, tag) == null) {
+ return;
+ }
+ mIconLogger.onIconHidden(getSlotName(index));
+ super.removeIcon(index, tag);
+ int viewIndex = getViewIndex(index, 0);
mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
}
- @Override
- public void setIcon(int index, StatusBarIcon icon) {
- if (icon == null) {
- removeIcon(index);
- return;
- }
- boolean isNew = getIcon(index) == null;
- super.setIcon(index, icon);
- if (isNew) {
- addSystemIcon(index, icon);
- } else {
- handleSet(index, 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));
}
- private void handleSet(int index, StatusBarIcon icon) {
- int viewIndex = getViewIndex(index);
- mIconLogger.onIconVisibility(getSlot(index), icon.visible);
- mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));
+ /**
+ * 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 @@
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 0000000..e854dd0
--- /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 1aa3a43..c773170 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 @@
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/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5975608..6b6ea10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -135,26 +135,34 @@
mFingerprintUnlockController = fingerprintUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);
+ mContainer.addOnLayoutChangeListener(this::onContainerLayout);
mNotificationPanelView = notificationPanelView;
notificationPanelView.setExpansionListener(this::onPanelExpansionChanged);
}
+ private void onContainerLayout(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mNotificationPanelView.setBouncerTop(mBouncer.getTop());
+ }
+
private void onPanelExpansionChanged(float expansion, boolean tracking) {
// We don't want to translate the bounce when:
// • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
// conserve the original animation.
// • The user quickly taps on the display and we show "swipe up to unlock."
// • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
+ // • Full-screen user switcher is displayed.
final boolean noLongerTracking = mLastTracking != tracking && !tracking;
if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
- || mBouncer.willDismissWithAction()) {
+ || mBouncer.willDismissWithAction()
+ || mStatusBar.isFullScreenUserSwitcherState()) {
mBouncer.setExpansion(0);
} else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
mBouncer.setExpansion(expansion);
if (expansion == 1) {
mBouncer.onFullyHidden();
} else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
- mBouncer.show(false /* resetSecuritySelection */, false /* notifyFalsing */);
+ mBouncer.show(false /* resetSecuritySelection */, false /* animated */);
} else if (noLongerTracking) {
// Notify that falsing manager should stop its session when user stops touching,
// even before the animation ends, to guarantee that we're not recording sensitive
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 0000000..c5a3a0d
--- /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 dab28d6..255e10e 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 @@
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 @@
* 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 @@
//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 @@
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/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index baeaaad..4c92d01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -40,7 +40,6 @@
import android.view.View;
import android.widget.TextView;
-import com.android.settingslib.Utils;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
@@ -85,17 +84,6 @@
private boolean mShowSeconds;
private Handler mSecondsHandler;
- /**
- * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
- * for text.
- */
- private boolean mUseWallpaperTextColor;
-
- /**
- * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
- */
- private int mNonAdaptedColor;
-
public Clock(Context context) {
this(context, null);
}
@@ -113,7 +101,6 @@
try {
mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
mShowDark = a.getBoolean(R.styleable.Clock_showDark, true);
- mNonAdaptedColor = getCurrentTextColor();
} finally {
a.recycle();
}
@@ -240,10 +227,7 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
- mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
- if (!mUseWallpaperTextColor) {
- setTextColor(mNonAdaptedColor);
- }
+ setTextColor(DarkIconDispatcher.getTint(area, this, tint));
}
@Override
@@ -258,25 +242,6 @@
0);
}
- /**
- * Sets whether the clock uses the wallpaperTextColor. If we're not using it, we'll revert back
- * to dark-mode-based/tinted colors.
- *
- * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
- */
- public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
- if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
- return;
- }
- mUseWallpaperTextColor = shouldUseWallpaperTextColor;
-
- if (mUseWallpaperTextColor) {
- setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
- } else {
- setTextColor(mNonAdaptedColor);
- }
- }
-
private void updateShowSeconds() {
if (mShowSeconds) {
// Wait until we have a display to start trying to show seconds.
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 58944c6..945ed76 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 @@
// 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/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index ef630c7..74a30fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -27,7 +27,6 @@
import android.util.AttributeSet;
import android.widget.TextView;
-import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -43,17 +42,6 @@
private String mLastText;
private String mDatePattern;
- /**
- * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
- * for text.
- */
- private boolean mUseWallpaperTextColor;
-
- /**
- * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
- */
- private int mNonAdaptedTextColor;
-
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -74,7 +62,6 @@
public DateView(Context context, AttributeSet attrs) {
super(context, attrs);
- mNonAdaptedTextColor = getCurrentTextColor();
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.DateView,
@@ -130,25 +117,6 @@
}
}
- /**
- * Sets whether the date view uses the wallpaperTextColor. If we're not using it, we'll revert
- * back to dark-mode-based/tinted colors.
- *
- * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
- */
- public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
- if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
- return;
- }
- mUseWallpaperTextColor = shouldUseWallpaperTextColor;
-
- if (mUseWallpaperTextColor) {
- setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
- } else {
- setTextColor(mNonAdaptedTextColor);
- }
- }
-
public void setDatePattern(String pattern) {
if (TextUtils.equals(pattern, mDatePattern)) {
return;
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 4533aa5..4c100cd 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.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 @@
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/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 7ffca173..d527465 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -323,10 +323,10 @@
if (notif != null) {
mNotificationManager.notifyAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
- notif, UserHandle.ALL);
+ notif, UserHandle.of(vol.getMountUserId()));
} else {
mNotificationManager.cancelAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
- UserHandle.ALL);
+ UserHandle.of(vol.getMountUserId()));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 1cbdfe8..7a9cdfd 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 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/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index ee6748e..a97effd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.volume;
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -29,6 +31,7 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.IAudioService;
import android.media.IVolumeController;
import android.media.VolumePolicy;
import android.media.session.MediaController.PlaybackInfo;
@@ -39,6 +42,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
@@ -73,9 +77,11 @@
public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
+
+ private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
private static final int DYNAMIC_STREAM_START_INDEX = 100;
private static final int VIBRATE_HINT_DURATION = 50;
- private static final AudioAttributes SONFICIATION_VIBRATION_ATTRIBUTES =
+ private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -100,6 +106,7 @@
private final W mWorker;
private final Context mContext;
private AudioManager mAudio;
+ private IAudioService mAudioService;
protected StatusBar mStatusBar;
private final NotificationManager mNoMan;
private final SettingObserver mObserver;
@@ -113,6 +120,7 @@
private boolean mShowA11yStream;
private boolean mShowVolumeDialog;
private boolean mShowSafetyWarning;
+ private long mLastToggledRingerOn;
private final NotificationManager mNotificationManager;
private boolean mDestroyed;
@@ -140,6 +148,8 @@
mReceiver.init();
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+ mAudioService = IAudioService.Stub.asInterface(
+ ServiceManager.getService(Context.AUDIO_SERVICE));
updateStatusBar();
boolean accessibilityVolumeStreamActive = context.getSystemService(
@@ -300,10 +310,24 @@
mShowSafetyWarning = safetyWarning;
}
- public void vibrate() {
+ @Override
+ public void scheduleTouchFeedback() {
+ mLastToggledRingerOn = System.currentTimeMillis();
+ }
+
+ private void playTouchFeedback() {
+ if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
+ try {
+ mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ }
+
+ public void vibrate(VibrationEffect effect) {
if (mHasVibrator) {
- mVibrator.vibrate(VibrationEffect.createOneShot(VIBRATE_HINT_DURATION,
- VibrationEffect.DEFAULT_AMPLITUDE), SONFICIATION_VIBRATION_ATTRIBUTES);
+ mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
}
}
@@ -425,7 +449,7 @@
for (int stream : STREAMS.keySet()) {
updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
- streamStateW(stream).levelMax = getAudioManagerStreamMaxVolume(stream);
+ streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
updateStreamMuteW(stream, mAudio.isStreamMute(stream));
final StreamState ss = streamStateW(stream);
ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
@@ -556,6 +580,11 @@
if (rm == mState.ringerModeInternal) return false;
mState.ringerModeInternal = rm;
Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
+
+ if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
+ playTouchFeedback();
+ }
+
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 71c7f80..6f71e55 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -22,6 +22,10 @@
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -41,13 +45,17 @@
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.MediaPlayer;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
@@ -126,6 +134,7 @@
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
+ private final Vibrator mVibrator;
private boolean mShowing;
private boolean mShowA11yStream;
@@ -146,6 +155,7 @@
mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+ mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
public void init(int windowType, Callback callback) {
@@ -203,6 +213,9 @@
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
mWindow.getDecorView().requestAccessibilityFocus();
+ if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true)) {
+ mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500);
+ }
})
.start();
});
@@ -223,12 +236,16 @@
mSettingsIcon = mDialog.findViewById(R.id.settings);
if (mRows.isEmpty()) {
+ if (!AudioSystem.isSingleVolume(mContext)) {
+ addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
+ R.drawable.ic_volume_accessibility, true, false);
+ }
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
if (!AudioSystem.isSingleVolume(mContext)) {
addRow(AudioManager.STREAM_RING,
R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
- addRow(AudioManager.STREAM_ALARM,
+ addRow(STREAM_ALARM,
R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false);
addRow(AudioManager.STREAM_VOICE_CALL,
R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false, false);
@@ -236,8 +253,6 @@
R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false);
addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system,
R.drawable.ic_volume_system_mute, false, false);
- addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
- R.drawable.ic_volume_accessibility, true, false);
}
} else {
addExistingRows();
@@ -287,11 +302,11 @@
if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
VolumeRow row = new VolumeRow();
initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
- int rowSize;
- if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1) {
- // A11y Stream should be the first in the list, so it's shown to start of other rows
- mDialogRowsView.addView(row.view, 0);
- mRows.add(rowSize - 2, row);
+ if (dynamic && mRows.size() > 2) {
+ // Dynamic Streams should be the first in the list, so they're shown to start of
+ // everything except a11y
+ mDialogRowsView.addView(row.view, 1);
+ mRows.add(1, row);
} else {
mDialogRowsView.addView(row.view);
mRows.add(row);
@@ -315,6 +330,11 @@
return row;
}
}
+ for (VolumeRow row : mRows) {
+ if (row.stream == STREAM_MUSIC) {
+ return row;
+ }
+ }
return mRows.get(0);
}
@@ -414,6 +434,7 @@
mRingerIcon.setOnClickListener(v -> {
Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING,
mRingerIcon.getTag());
+ Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
if (ss == null) {
return;
@@ -424,7 +445,6 @@
final boolean hasVibrator = mController.hasVibrator();
if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
if (hasVibrator) {
- mController.vibrate();
newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
} else {
newRingerMode = AudioManager.RINGER_MODE_SILENT;
@@ -438,30 +458,56 @@
}
}
updateRingerH();
-
+ provideTouchFeedbackH(newRingerMode);
mController.setRingerMode(newRingerMode, false);
maybeShowToastH(newRingerMode);
});
updateRingerH();
}
+
+ private void provideTouchFeedbackH(int newRingerMode) {
+ VibrationEffect effect = null;
+ switch (newRingerMode) {
+ case RINGER_MODE_NORMAL:
+ mController.scheduleTouchFeedback();
+ break;
+ case RINGER_MODE_SILENT:
+ effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ break;
+ case RINGER_MODE_VIBRATE:
+ default:
+ effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ }
+ if (effect != null) {
+ mController.vibrate(effect);
+ }
+ }
+
private void maybeShowToastH(int newRingerMode) {
int seenToastCount = Prefs.getInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, 0);
if (seenToastCount > VolumePrefs.SHOW_RINGER_TOAST_COUNT) {
return;
}
- int toastText;
+ CharSequence toastText = null;
switch (newRingerMode) {
case RINGER_MODE_NORMAL:
- toastText = R.string.volume_dialog_ringer_guidance_ring;
+ final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+ if (ss != null) {
+ toastText = mContext.getString(
+ R.string.volume_dialog_ringer_guidance_ring,
+ Utils.formatPercentage(ss.level, ss.levelMax));
+ }
break;
case RINGER_MODE_SILENT:
- toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent;
+ toastText = mContext.getString(
+ com.android.internal.R.string.volume_dialog_ringer_guidance_silent);
break;
case RINGER_MODE_VIBRATE:
default:
- toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
+ toastText = mContext.getString(
+ com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate);
}
Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
@@ -538,7 +584,7 @@
}
private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
- boolean isActive = row == activeRow;
+ boolean isActive = row.stream == activeRow.stream;
if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
return mShowA11yStream;
}
@@ -550,7 +596,18 @@
return true;
}
- return row.defaultStream || isActive;
+ if (isActive) {
+ return true;
+ }
+
+ if (row.defaultStream) {
+ return activeRow.stream == STREAM_RING
+ || activeRow.stream == STREAM_ALARM
+ || activeRow.stream == STREAM_VOICE_CALL
+ || activeRow.stream == STREAM_ACCESSIBILITY;
+ }
+
+ return false;
}
private void updateRowsH(final VolumeRow activeRow) {
@@ -670,7 +727,8 @@
if (mActiveStream != state.activeStream) {
mPrevActiveStream = mActiveStream;
mActiveStream = state.activeStream;
- updateRowsH(getActiveRow());
+ VolumeRow activeRow = getActiveRow();
+ updateRowsH(activeRow);
rescheduleTimeoutH();
}
for (VolumeRow row : mRows) {
@@ -696,7 +754,7 @@
final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY;
final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
- final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
+ final boolean isAlarmStream = row.stream == STREAM_ALARM;
final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
final boolean isRingVibrate = isRingStream
&& mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
@@ -921,6 +979,21 @@
}
}
+ private Runnable mSinglePress = new Runnable() {
+ @Override
+ public void run() {
+ mRingerIcon.setPressed(true);
+ mRingerIcon.postOnAnimationDelayed(mSingleUnpress, 200);
+ }
+ };
+
+ private Runnable mSingleUnpress = new Runnable() {
+ @Override
+ public void run() {
+ mRingerIcon.setPressed(false);
+ }
+ };
+
private final VolumeDialogController.Callbacks mControllerCallbackH
= new VolumeDialogController.Callbacks() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
index 173400f..434327c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
@@ -43,7 +43,7 @@
public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms";
public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification";
- public static final int SHOW_RINGER_TOAST_COUNT = 9;
+ public static final int SHOW_RINGER_TOAST_COUNT = 12;
public static final boolean DEFAULT_SHOW_HEADERS = true;
public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;
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 4b455ba..149f2de 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 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 @@
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 @@
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 @@
}
@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 @@
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 23a3405..ab042d4 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.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.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.List;
import java.util.function.Consumer;
@SmallTest
@@ -274,4 +278,24 @@
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 64f34e0..78cceeb 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
}
@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 @@
@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 @@
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/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index c437021..5e27fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -35,6 +35,7 @@
import android.app.NotificationChannel;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.media.session.MediaSession;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -61,6 +62,7 @@
private static final int UID_NORMAL = 123;
private static final int UID_ALLOW_DURING_SETUP = 456;
private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+ private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
private final StatusBarNotification mMockStatusBarNotification =
mock(StatusBarNotification.class);
@@ -275,6 +277,7 @@
@Test
public void testShouldFilterHiddenNotifications() {
+ initStatusBarNotification(false);
// setup
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
@@ -289,6 +292,33 @@
assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
}
+ @Test
+ public void testIsExemptFromDndVisualSuppression_foreground() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getKey()).thenReturn(
+ TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+ Notification n = mMockStatusBarNotification.getNotification();
+ n.flags = Notification.FLAG_FOREGROUND_SERVICE;
+
+ assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+ assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+ }
+
+ @Test
+ public void testIsExemptFromDndVisualSuppression_media() {
+ initStatusBarNotification(false);
+ when(mMockStatusBarNotification.getKey()).thenReturn(
+ TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+ Notification n = mMockStatusBarNotification.getNotification();
+ Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ n = nb.build();
+ when(mMockStatusBarNotification.getNotification()).thenReturn(n);
+
+ assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+ assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -318,6 +348,13 @@
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+ } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
+ outRanking.populate(key, outRanking.getRank(),
+ outRanking.matchesInterruptionFilter(),
+ outRanking.getVisibilityOverride(), 255,
+ outRanking.getImportance(), outRanking.getImportanceExplanation(),
+ outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
} else {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
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 21f6750..0ef2d051 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 @@
@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 @@
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 @@
@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 @@
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 c2cb5b9..5b54e4d 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.MockitoRule;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -87,6 +86,7 @@
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 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 @@
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 @@
@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 @@
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 @@
@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 @@
@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 @@
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 @@
@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 @@
@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 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 @@
(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 @@
@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 @@
@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 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 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 @@
@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 @@
@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 @@
@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 @@
@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 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 @@
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 @@
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 @@
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 @@
null /* checkSaveListener */,
null /* onSettingsClick */,
null /* onAppSettingsClick */,
- null /* nonBlockablePkgs */,
+ false /* isNonblockable */,
true /* isForBlockingHelper */,
false /* isUserSentimentNegative */);
@@ -433,8 +433,7 @@
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 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 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 @@
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 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 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 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 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 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 @@
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 @@
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 @@
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 @@
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 @@
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,30 @@
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());
+ }
+
+ @Test
+ public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
+ final String settingsText = "work chats";
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+ ri.activityInfo.name = "something";
+ List<ResolveInfo> ris = new ArrayList<>();
+ ris.add(ri);
+ when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
+ Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+ .setSettingsText(settingsText).build();
+ StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+ 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null, true,
+ true);
final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
assertEquals(GONE, settingsLink.getVisibility());
}
@@ -710,8 +730,7 @@
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 +742,7 @@
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 +754,7 @@
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 +765,7 @@
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 2792d8c..07ac11b 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 @@
@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 5db7792..72b0156 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.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 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);
-
- manager.onIconAdded(0, "test_slot", false, icon);
- verify(manager.mMock).set(eq(icon));
+ testCallOnAdd_forManager(manager);
}
- private static class TestDarkIconManager extends DarkIconManager {
- private final StatusBarIconView mMock;
+ 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));
+
+ 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 StatusBarIconHolder holderForType(int type) {
+ switch (type) {
+ case TYPE_MOBILE:
+ return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class));
+
+ case TYPE_WIFI:
+ return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class));
+
+ 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);
+ }
+
+ @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 static class TestIconManager extends IconManager {
-
- private final StatusBarIconView mMock;
-
+ 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/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 4a66bb7..b31a2dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -41,6 +41,7 @@
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -71,6 +72,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -91,6 +93,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -98,6 +101,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import org.junit.Before;
@@ -134,6 +138,7 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
private TestableStatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
@@ -146,9 +151,7 @@
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(DeviceProvisionedController.class);
- mDependency.injectMockDependency(NotificationGroupManager.class);
mDependency.injectMockDependency(NotificationGutsManager.class);
- mDependency.injectMockDependency(NotificationRemoteInputManager.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectMockDependency(ForegroundServiceController.class);
mDependency.injectTestDependency(NotificationViewHierarchyManager.class,
@@ -202,7 +205,12 @@
mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
mEntryManager, mScrimController, mFingerprintUnlockController,
- mock(ActivityLaunchAnimator.class), mKeyguardViewMediator);
+ mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
+ mock(NotificationRemoteInputManager.class), mock(NotificationGroupManager.class),
+ mock(FalsingManager.class), mock(StatusBarWindowManager.class),
+ mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
+ mock(NotificationShelf.class), mLockscreenUserManager,
+ mock(CommandQueue.class));
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager,
@@ -529,11 +537,7 @@
@Test
@RunWithLooper(setAsMainLooper = true)
public void testUpdateKeyguardState_DoesNotCrash() {
- mStatusBar.mStatusBarWindow = mock(StatusBarWindowView.class);
mStatusBar.mState = StatusBarState.KEYGUARD;
- mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
- mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
- mStatusBar.mLockscreenUserManager = mock(NotificationLockscreenUserManager.class);
when(mStatusBar.mLockscreenUserManager.getCurrentProfiles()).thenReturn(
new SparseArray<>());
mStatusBar.updateKeyguardState(false, false);
@@ -541,9 +545,6 @@
@Test
public void testFingerprintNotification_UpdatesScrims() {
- mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
- mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
- mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
mStatusBar.notifyFpAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
@@ -629,6 +630,32 @@
verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
}
+ @Test
+ public void testSetState_changesIsFullScreenUserSwitcherState() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ assertFalse(mStatusBar.isFullScreenUserSwitcherState());
+
+ mStatusBar.setBarStateForTest(StatusBarState.FULLSCREEN_USER_SWITCHER);
+ assertTrue(mStatusBar.isFullScreenUserSwitcherState());
+ }
+
+ @Test
+ public void testShowKeyguardImplementation_setsState() {
+ when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
+
+ mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+
+ // By default, showKeyguardImpl sets state to KEYGUARD.
+ mStatusBar.showKeyguardImpl();
+ assertTrue(mStatusBar.mState == StatusBarState.KEYGUARD);
+
+ // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
+ mStatusBar.mUserSwitcherController = mock(UserSwitcherController.class);
+ when(mStatusBar.mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
+ mStatusBar.showKeyguardImpl();
+ assertTrue(mStatusBar.mState == StatusBarState.FULLSCREEN_USER_SWITCHER);
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -640,7 +667,16 @@
NotificationViewHierarchyManager viewHierarchyManager,
TestableNotificationEntryManager entryManager, ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController,
- ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator) {
+ ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ NotificationGroupManager notificationGroupManager,
+ FalsingManager falsingManager,
+ StatusBarWindowManager statusBarWindowManager,
+ NotificationIconAreaController notificationIconAreaController,
+ DozeScrimController dozeScrimController,
+ NotificationShelf notificationShelf,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ CommandQueue commandQueue) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
@@ -660,6 +696,15 @@
mActivityLaunchAnimator = launchAnimator;
mKeyguardViewMediator = keyguardViewMediator;
mClearAllEnabled = true;
+ mRemoteInputManager = notificationRemoteInputManager;
+ mGroupManager = notificationGroupManager;
+ mFalsingManager = falsingManager;
+ mStatusBarWindowManager = statusBarWindowManager;
+ mNotificationIconAreaController = notificationIconAreaController;
+ mDozeScrimController = dozeScrimController;
+ mNotificationShelf = notificationShelf;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mCommandQueue = commandQueue;
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
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 6b501af..8e34685 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 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 @@
}
@Override
- public void setIconVisibility(String slotTty, boolean b) {
-
+ public void setSignalIcon(String slot, WifiIconState state) {
}
@Override
- public void removeIcon(String slot) {
-
+ public void setMobileIcons(String slot, List<MobileIconState> states) {
}
+
+ @Override
+ public void setIconVisibility(String slotTty, boolean b) {
+ }
+
+ @Override
+ public void removeIcon(String slot, int tag) {
+ }
+
+ @Override
+ public void removeAllIconsForSlot(String slot) {
+ }
+
}
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
deleted file mode 100644
index 1aa1af3..0000000
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
deleted file mode 100644
index 1aa1af3..0000000
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
deleted file mode 100644
index 1aa1af3..0000000
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
deleted file mode 100644
index 1aa1af3..0000000
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
deleted file mode 100644
index 1aa1af3..0000000
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 9c74188..934ad88 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -249,12 +249,12 @@
optional int32 num_wificond_crashes = 54;
// Indicates the number of times an error was encountered in
- // Wifi HAL when wifi was turned on.
- optional int32 num_wifi_on_failure_due_to_hal = 55;
+ // Wifi HAL on |WifiNative.setupInterfaceForClientMode|.
+ optional int32 num_setup_client_interface_failure_due_to_hal = 55;
// Indicates the number of times an error was encountered in
- // Wificond when wifi was turned on.
- optional int32 num_wifi_on_failure_due_to_wificond = 56;
+ // Wificond on |WifiNative.setupInterfaceForClientMode|.
+ optional int32 num_setup_client_interface_failure_due_to_wificond = 56;
// Wi-Fi Aware metrics
optional WifiAwareLog wifi_aware_log = 57;
@@ -385,6 +385,43 @@
// Histogram counting instances of scans with N many 802.11mc (RTT) supporting APs
repeated NumConnectableNetworksBucket observed_80211mc_supporting_aps_in_scan_histogram = 95;
+
+ // Total number of times supplicant crashed.
+ optional int32 num_supplicant_crashes = 96;
+
+ // Total number of times hostapd crashed.
+ optional int32 num_hostapd_crashes = 97;
+
+ // Indicates the number of times an error was encountered in
+ // supplicant on |WifiNative.setupInterfaceForClientMode|.
+ optional int32 num_setup_client_interface_failure_due_to_supplicant = 98;
+
+ // Indicates the number of times an error was encountered in
+ // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|.
+ optional int32 num_setup_soft_ap_interface_failure_due_to_hal = 99;
+
+ // Indicates the number of times an error was encountered in
+ // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|.
+ optional int32 num_setup_soft_ap_interface_failure_due_to_wificond = 100;
+
+ // Indicates the number of times an error was encountered in
+ // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|.
+ optional int32 num_setup_soft_ap_interface_failure_due_to_hostapd = 101;
+
+ // Indicates the number of times we got an interface down in client mode.
+ optional int32 num_client_interface_down = 102;
+
+ // 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/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8941b49..de112f9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -853,11 +853,7 @@
if (mSecurityPolicy.findA11yWindowInfoById(windowId) == null) {
return null;
}
- IBinder token = mGlobalWindowTokens.get(windowId);
- if (token != null) {
- return token;
- }
- return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ return findWindowTokenLocked(windowId);
}
}
@@ -2527,6 +2523,14 @@
getInteractionBridge().clearAccessibilityFocusNotLocked(windowId);
}
+ private IBinder findWindowTokenLocked(int windowId) {
+ IBinder token = mGlobalWindowTokens.get(windowId);
+ if (token != null) {
+ return token;
+ }
+ return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ }
+
private int findWindowIdLocked(IBinder token) {
final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
if (globalIndex >= 0) {
@@ -2986,7 +2990,7 @@
// the accessibility layer reports which are windows
// that a sighted user can touch.
default: {
- return isRetrievalAllowingWindow(event.getWindowId());
+ return isRetrievalAllowingWindowLocked(event.getWindowId());
}
}
}
@@ -3438,7 +3442,8 @@
public boolean canGetAccessibilityNodeInfoLocked(
AbstractAccessibilityServiceConnection service, int windowId) {
- return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
+ return canRetrieveWindowContentLocked(service)
+ && isRetrievalAllowingWindowLocked(windowId);
}
public boolean canRetrieveWindowsLocked(AbstractAccessibilityServiceConnection service) {
@@ -3523,17 +3528,40 @@
|| userId == UserHandle.USER_CURRENT_OR_SELF);
}
- private boolean isRetrievalAllowingWindow(int windowId) {
+ private boolean isRetrievalAllowingWindowLocked(int windowId) {
// The system gets to interact with any window it wants.
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
return true;
}
+ if (Binder.getCallingUid() == Process.SHELL_UID) {
+ if (!isShellAllowedToRetrieveWindowLocked(windowId)) {
+ return false;
+ }
+ }
if (windowId == mActiveWindowId) {
return true;
}
return findA11yWindowInfoById(windowId) != null;
}
+ private boolean isShellAllowedToRetrieveWindowLocked(int windowId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ IBinder windowToken = findWindowTokenLocked(windowId);
+ if (windowToken == null) {
+ return false;
+ }
+ int userId = mWindowManagerService.getWindowOwnerUserId(windowToken);
+ if (userId == UserHandle.USER_NULL) {
+ return false;
+ }
+ return !mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public AccessibilityWindowInfo findA11yWindowInfoById(int windowId) {
return mA11yWindowInfoById.get(windowId);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d4ecd8b..3253f2e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
-import static com.android.server.autofill.Helper.bundleToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sPartitionMaxCount;
@@ -193,8 +192,7 @@
if (disabledBefore == disabledNow) {
// Nothing changed, do nothing.
if (sDebug) {
- Slog.d(TAG, "Autofill restriction did not change for user " + userId + ": "
- + bundleToString(newRestrictions));
+ Slog.d(TAG, "Autofill restriction did not change for user " + userId);
return;
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index a202aaf..78526f5 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -79,28 +79,6 @@
throw new UnsupportedOperationException("contains static members only");
}
- static void append(StringBuilder builder, Bundle bundle) {
- if (bundle == null || !sVerbose) {
- builder.append("null");
- return;
- }
- final Set<String> keySet = bundle.keySet();
- builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
- for (String key : keySet) {
- final Object value = bundle.get(key);
- builder.append(' ').append(key).append('=');
- builder.append((value instanceof Object[])
- ? Arrays.toString((Objects[]) value) : value);
- }
- builder.append(']');
- }
-
- static String bundleToString(Bundle bundle) {
- final StringBuilder builder = new StringBuilder();
- append(builder, bundle);
- return builder.toString();
- }
-
@Nullable
static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
if (set == null) return null;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 706fb1a..e14584f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2502,8 +2502,10 @@
}
pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
- pw.print(prefix); pw.print("mClientState: "); pw.println(
- Helper.bundleToString(mClientState));
+ if (mClientState != null) {
+ pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw
+ .println(" bytes");
+ }
pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
pw.print(prefix); pw.print("mUrlBar: ");
if (mUrlBar == null) {
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index 4de2c9b..49fa1cc 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -21,6 +21,7 @@
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.KeyValueSettingObserver;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -31,6 +32,8 @@
* are represented as a comma-delimited key value list.
*/
public class BackupAgentTimeoutParameters extends KeyValueSettingObserver {
+ private static final String TAG = "BackupAgentTimeout";
+
@VisibleForTesting
public static final String SETTING = Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS;
@@ -120,30 +123,50 @@
public long getKvBackupAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
+ }
return mKvBackupAgentTimeoutMillis;
}
}
public long getFullBackupAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
+ }
return mFullBackupAgentTimeoutMillis;
}
}
public long getSharedBackupAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(
+ TAG,
+ "getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
+ }
return mSharedBackupAgentTimeoutMillis;
}
}
public long getRestoreAgentTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
+ }
return mRestoreAgentTimeoutMillis;
}
}
public long getRestoreAgentFinishedTimeoutMillis() {
synchronized (mLock) {
+ if (BackupManagerService.DEBUG_SCHEDULING) {
+ Slog.v(
+ TAG,
+ "getRestoreAgentFinishedTimeoutMillis(): "
+ + mRestoreAgentFinishedTimeoutMillis);
+ }
return mRestoreAgentFinishedTimeoutMillis;
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d6f6c6c..bd51af2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -215,13 +215,6 @@
// Timeout interval for deciding that a bind or clear-data has taken too long
private static final long TIMEOUT_INTERVAL = 10 * 1000;
- // Timeout intervals for agent backup & restore operations
- public static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
- public static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
- public static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
- public static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
- public static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
-
// User confirmation timeout for a full backup/restore operation. It's this long in
// order to give them time to enter the backup password.
private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
@@ -232,6 +225,7 @@
private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
private BackupManagerConstants mConstants;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private Context mContext;
private PackageManager mPackageManager;
private IPackageManager mPackageManagerBinder;
@@ -315,6 +309,10 @@
return mConstants;
}
+ public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+ return mAgentTimeoutParameters;
+ }
+
public Context getContext() {
return mContext;
}
@@ -799,6 +797,10 @@
mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+ mAgentTimeoutParameters = new
+ BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+ mAgentTimeoutParameters.start();
+
// spin up the backup/restore handler thread
mBackupHandler = new BackupHandler(this, backupThread.getLooper());
@@ -3407,7 +3409,7 @@
}
mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
- TIMEOUT_RESTORE_INTERVAL);
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
}
return mActiveRestoreSession;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 7b021c6..aabe7f6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -191,4 +191,7 @@
void dump(FileDescriptor fd, PrintWriter pw, String[] args);
IBackupManager getBackupManagerBinder();
+
+ // Gets access to the backup/restore agent timeout parameters.
+ BackupAgentTimeoutParameters getAgentTimeoutParameters();
}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 4755877..7f0030a 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -19,6 +19,7 @@
import android.os.SELinux;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
import com.android.server.backup.utils.FullBackupUtils;
import libcore.io.IoUtils;
@@ -59,6 +60,7 @@
private ParcelFileDescriptor mSavedState;
private ParcelFileDescriptor mBackupData;
private ParcelFileDescriptor mNewState;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
@@ -81,6 +83,9 @@
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public void backupOnePackage() throws IOException {
@@ -148,8 +153,9 @@
// Return true on backup success, false otherwise
private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) {
int token = mBackupManagerService.generateRandomIntegerToken();
+ long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
try {
- mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+ mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
OP_TYPE_BACKUP_WAIT);
// Start backup and wait for BackupManagerService to get callback for success or timeout
@@ -231,14 +237,14 @@
}
private void writeBackupData() throws IOException {
-
int token = mBackupManagerService.generateRandomIntegerToken();
+ long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
ParcelFileDescriptor[] pipes = null;
try {
pipes = ParcelFileDescriptor.createPipe();
- mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+ mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
OP_TYPE_BACKUP_WAIT);
// We will have to create a runnable that will read the manifest and backup data we
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 0582aba..5694659 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -25,9 +25,6 @@
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService
- .TIMEOUT_SHARED_BACKUP_INTERVAL;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -44,9 +41,11 @@
import android.util.Slog;
import android.util.StringBuilderPrinter;
+import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
-import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.utils.FullBackupUtils;
import java.io.BufferedOutputStream;
@@ -75,6 +74,7 @@
private final long mQuota;
private final int mOpToken;
private final int mTransportFlags;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
class FullBackupRunner implements Runnable {
@@ -137,8 +137,8 @@
final boolean isSharedStorage =
mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
final long timeout = isSharedStorage ?
- TIMEOUT_SHARED_BACKUP_INTERVAL :
- TIMEOUT_FULL_BACKUP_INTERVAL;
+ mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
if (DEBUG) {
Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
@@ -180,6 +180,9 @@
mQuota = quota;
mOpToken = opToken;
mTransportFlags = transportFlags;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public int preflightCheck() throws RemoteException {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index 40b6967..bc7d9fc 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -19,7 +19,6 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -33,6 +32,8 @@
import android.util.Slog;
import com.android.internal.backup.IObbBackupService;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
@@ -46,10 +47,14 @@
private BackupManagerService backupManagerService;
volatile IObbBackupService mService;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public FullBackupObbConnection(BackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
mService = null;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public void establish() {
@@ -75,8 +80,10 @@
try {
pipes = ParcelFileDescriptor.createPipe();
int token = backupManagerService.generateRandomIntegerToken();
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_BACKUP_WAIT);
+ token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
mService.backupObbs(pkg.packageName, pipes[1], token,
backupManagerService.getBackupManagerBinder());
FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2c2dd85..a40afc3 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -22,7 +22,6 @@
import static com.android.server.backup.BackupManagerService.OP_PENDING;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -43,7 +42,9 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
import com.android.server.backup.BackupManagerService;
@@ -146,6 +147,7 @@
private volatile boolean mIsDoingBackup;
private volatile boolean mCancelAll;
private final int mCurrentOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
TransportClient transportClient,
@@ -167,6 +169,9 @@
mUserInitiated = userInitiated;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
if (backupManagerService.isBackupOperationInProgress()) {
if (DEBUG) {
@@ -698,9 +703,11 @@
@Override
public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
int result;
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
backupManagerService.prepareOperationTimeout(
- mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+ mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
backupManagerService.addBackupTrace("preflighting");
if (MORE_DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
@@ -713,7 +720,7 @@
// timeout had been produced. In case of a real backstop timeout, mResult
// will still contain the value it was constructed with, AGENT_ERROR, which
// intentionaly falls into the "just report failure" code.
- mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
long totalSize = mResult.get();
// If preflight timed out, mResult will contain error code as int.
@@ -769,8 +776,10 @@
@Override
public long getExpectedSizeOrErrorCode() {
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
return mResult.get();
} catch (InterruptedException e) {
return BackupTransport.NO_MORE_DATA;
@@ -863,8 +872,10 @@
// If preflight succeeded, returns positive number - preflight size,
// otherwise return negative error code.
long getPreflightResultBlocking() {
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
if (mIsCancelled) {
return BackupManager.ERROR_BACKUP_CANCELLED;
}
@@ -879,8 +890,10 @@
}
int getBackupResultBlocking() {
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
if (mIsCancelled) {
return BackupManager.ERROR_BACKUP_CANCELLED;
}
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 136fada..69f08ae 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -19,7 +19,6 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
import android.app.backup.RestoreSet;
import android.content.Intent;
@@ -33,7 +32,9 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
@@ -81,10 +82,14 @@
public static final int MSG_OP_COMPLETE = 21;
private final BackupManagerService backupManagerService;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
super(looper);
this.backupManagerService = backupManagerService;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public void handleMessage(Message msg) {
@@ -322,7 +327,8 @@
// Done: reset the session timeout clock
removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
+ sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
params.listener.onFinished(callerLogString);
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 11394e66..ac605b1 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -24,7 +24,6 @@
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
@@ -55,8 +54,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
@@ -142,6 +143,7 @@
private boolean mFinished;
private final boolean mUserInitiated;
private final boolean mNonIncremental;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private volatile boolean mCancelAll;
@@ -162,6 +164,9 @@
mPendingFullBackups = pendingFullBackups;
mUserInitiated = userInitiated;
mNonIncremental = nonIncremental;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -711,8 +716,10 @@
// Initiate the target's backup pass
backupManagerService.addBackupTrace("setting timeout");
+ long kvBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+ mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
backupManagerService.addBackupTrace("calling agent doBackup()");
agent.doBackup(
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4f3a9d..a8c7ce6 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -18,10 +18,11 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
@@ -37,18 +38,24 @@
private BackupManagerService backupManagerService;
final CountDownLatch mLatch;
private final int mCurrentOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
int currentOpToken) {
this.backupManagerService = backupManagerService;
mLatch = new CountDownLatch(1);
mCurrentOpToken = currentOpToken;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
void await() {
boolean latched = false;
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
- latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+ latched = mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Slog.w(TAG, "Interrupted!");
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index c1a1c1d..6bc7530 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -23,9 +23,6 @@
import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
-import static com.android.server.backup.BackupManagerService
- .TIMEOUT_SHARED_BACKUP_INTERVAL;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import android.app.ApplicationThreadConstants;
@@ -40,13 +37,17 @@
import android.content.pm.Signature;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BytesReadListener;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -56,8 +57,11 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
/**
* Full restore engine, used by both adb restore and transport-based full restore.
@@ -121,6 +125,8 @@
final int mEphemeralOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+
public FullRestoreEngine(BackupManagerService backupManagerService,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
@@ -135,6 +141,9 @@
mAllowObbs = allowObbs;
mBuffer = new byte[32 * 1024];
mBytes = 0;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public IBackupAgent getAgent() {
@@ -320,12 +329,17 @@
pkg, 0);
// If we haven't sent any data to this app yet, we probably
- // need to clear it first. Check that.
+ // need to clear it first. Check that.
if (!mClearedPackages.contains(pkg)) {
- // apps with their own backup agents are
- // responsible for coherently managing a full
- // restore.
- if (mTargetApp.backupAgentName == null) {
+ // Apps with their own backup agents are responsible for coherently
+ // managing a full restore.
+ // In some rare cases they can't, especially in case of deferred
+ // restore. In this case check whether this app should be forced to
+ // clear up.
+ // TODO: Fix this properly with manifest parameter.
+ boolean forceClear = shouldForceClearAppDataOnFullRestore(
+ mTargetApp.packageName);
+ if (mTargetApp.backupAgentName == null || forceClear) {
if (DEBUG) {
Slog.d(TAG,
"Clearing app data preparatory to full restore");
@@ -381,8 +395,8 @@
long toCopy = info.size;
final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
final long timeout = isSharedStorage ?
- TIMEOUT_SHARED_BACKUP_INTERVAL :
- TIMEOUT_RESTORE_INTERVAL;
+ mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
try {
mBackupManagerService.prepareOperationTimeout(token,
timeout,
@@ -623,6 +637,24 @@
return true;
}
+ /**
+ * Returns whether the package is in the list of the packages for which clear app data should
+ * be called despite the fact that they have backup agent.
+ *
+ * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+ */
+ private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
+ String packageListString = Settings.Secure.getString(
+ mBackupManagerService.getContext().getContentResolver(),
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+ if (TextUtils.isEmpty(packageListString)) {
+ return false;
+ }
+
+ List<String> packages = Arrays.asList(packageListString.split(";"));
+ return packages.contains(packageName);
+ }
+
void sendOnRestorePackage(String name) {
if (mObserver != null) {
try {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index dacde0b..77163d3 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,8 +16,6 @@
package com.android.server.backup.restore;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
@@ -28,8 +26,8 @@
import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import android.app.ApplicationThreadConstants;
@@ -48,7 +46,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -101,6 +101,7 @@
private byte[] mWidgetData = null;
private long mBytes;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
// Runner that can be placed on a separate thread to do in-process invocation
// of the "restore finished" API asynchronously. Used by adb restore.
@@ -155,6 +156,9 @@
mAgentPackage = null;
mTargetApp = null;
mObbConnection = new FullBackupObbConnection(backupManagerService);
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
// Which packages we've already wiped data on. We prepopulate this
// with a whitelist of packages known to be unclearable.
@@ -643,9 +647,11 @@
if (okay) {
boolean agentSuccess = true;
long toCopy = info.size;
+ long restoreAgentTimeoutMillis =
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
try {
mBackupManagerService.prepareOperationTimeout(
- token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT);
+ token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
if (DEBUG) {
@@ -820,10 +826,12 @@
// In the adb restore case, we do restore-finished here
if (doRestoreFinished) {
final int token = mBackupManagerService.generateRandomIntegerToken();
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
mBackupManagerService, token);
mBackupManagerService.prepareOperationTimeout(
- token, TIMEOUT_FULL_BACKUP_INTERVAL, latch, OP_TYPE_RESTORE_WAIT);
+ token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
if (mTargetApp.processName.equals("system")) {
if (MORE_DEBUG) {
Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 4b467e5..12d72d8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -23,9 +23,6 @@
import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService
- .TIMEOUT_RESTORE_FINISHED_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -56,9 +53,11 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
import com.android.server.backup.PackageManagerBackupAgent;
@@ -160,6 +159,7 @@
ParcelFileDescriptor mNewState;
private final int mEphemeralOpToken;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
// This task can assume that the wakelock is properly held for it and doesn't have to worry
// about releasing it.
@@ -190,6 +190,9 @@
mFinished = false;
mDidLaunch = false;
mListener = listener;
+ mAgentTimeoutParameters = Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
if (targetPackage != null) {
// Single package restore
@@ -760,8 +763,9 @@
// Kick off the restore, checking for hung agents. The timeout or
// the operationComplete() callback will schedule the next step,
// so we do not do that here.
+ long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_RESTORE_WAIT);
+ mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
mAgent.doRestore(mBackupData, appVersionCode, mNewState,
mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
} catch (Exception e) {
@@ -813,9 +817,11 @@
Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
}
try {
+ long restoreAgentFinishedTimeoutMillis =
+ mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis();
backupManagerService
.prepareOperationTimeout(mEphemeralOpToken,
- TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
+ restoreAgentFinishedTimeoutMillis, this,
OP_TYPE_RESTORE_WAIT);
mAgent.doRestoreFinished(mEphemeralOpToken,
backupManagerService.getBackupManagerBinder());
@@ -1109,9 +1115,10 @@
} else {
// We were invoked via an active restore session, not by the Package
// Manager, so start up the session timeout again.
+ long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
backupManagerService.getBackupHandler().sendEmptyMessageDelayed(
MSG_RESTORE_SESSION_TIMEOUT,
- TIMEOUT_RESTORE_INTERVAL);
+ restoreAgentTimeoutMillis);
}
// Kick off any work that may be needed regarding app widget restores
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d09a161..06c1056 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -931,7 +931,7 @@
return mPort;
}
- public FileDescriptor getSocket() {
+ public FileDescriptor getFileDescriptor() {
return mSocket;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 08e5d44..f756afb 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 @@
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 @@
+ 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 @@
.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 @@
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 @@
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 @@
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 @@
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 724dd3f..47d0423 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 @@
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 @@
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 @@
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 @@
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 b2872e4..7ee20fa 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.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 @@
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 b338029..8ecd93e 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 106b37f..bef650b 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -285,6 +285,16 @@
return false;
}
+ /**
+ * @return the root task of the lock task.
+ */
+ TaskRecord getRootTask() {
+ if (mLockTaskModeTasks.isEmpty()) {
+ return null;
+ }
+ return mLockTaskModeTasks.get(0);
+ }
+
private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
// TODO: Double check what's going on here. If the task is already in lock task mode, it's
// likely whitelisted, so will return false below.
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index fcf00ce..1d305fb 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -1155,6 +1155,11 @@
}
}
+ // If we're in lock task mode, ignore the root task
+ if (task == mService.mLockTaskController.getRootTask()) {
+ return false;
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 73a7c3eb..9df321c 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -42,18 +42,13 @@
class RecentsAnimation implements RecentsAnimationCallbacks {
private static final String TAG = RecentsAnimation.class.getSimpleName();
- private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
-
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final ActivityStartController mActivityStartController;
private final WindowManagerService mWindowManager;
private final UserController mUserController;
- private final Handler mHandler;
private final int mCallingPid;
- private final Runnable mCancelAnimationRunnable;
-
// The stack to restore the home stack behind when the animation is finished
private ActivityStack mRestoreHomeBehindStack;
@@ -63,16 +58,9 @@
mService = am;
mStackSupervisor = stackSupervisor;
mActivityStartController = activityStartController;
- mHandler = new Handler(mStackSupervisor.mLooper);
mWindowManager = wm;
mUserController = userController;
mCallingPid = callingPid;
-
- mCancelAnimationRunnable = () -> {
- // The caller has not finished the animation in a predefined amount of time, so
- // force-cancel the animation
- mWindowManager.cancelRecentsAnimation();
- };
}
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
@@ -133,10 +121,6 @@
// duration of the gesture that is driven by the recents component
homeActivity.mLaunchTaskBehind = true;
- // Post a timeout for the animation. This needs to happen before initializing the
- // recents animation on the WM side since we may decide to cancel the animation there
- mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
-
// Fetch all the surface controls and pass them to the client to get the animation
// started
mWindowManager.cancelRecentsAnimation();
@@ -157,7 +141,6 @@
@Override
public void onAnimationFinished(boolean moveHomeToTop) {
- mHandler.removeCallbacks(mCancelAnimationRunnable);
synchronized (mService) {
if (mWindowManager.getRecentsAnimationController() == null) return;
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 32b1d1a..7a6d964 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -17,6 +17,7 @@
package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.ProgramList;
@@ -87,8 +88,9 @@
mTuner.close();
}
- void startProgramListUpdates(@NonNull ProgramList.Filter filter) {
- mProgramListFilter.set(Objects.requireNonNull(filter));
+ void startProgramListUpdates(@Nullable ProgramList.Filter filter) {
+ if (filter == null) filter = new ProgramList.Filter();
+ mProgramListFilter.set(filter);
sendProgramListUpdate();
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 6919282..9730c9a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -232,6 +232,7 @@
* HAL implementation instance. */
1, // numTuners
1, // numAudioSources
+ false, // isInitializationRequired
false, // isCaptureSupported
amfmConfigToBands(amfmConfig),
@@ -374,7 +375,9 @@
);
}
- static @NonNull ProgramFilter programFilterToHal(@NonNull ProgramList.Filter filter) {
+ static @NonNull ProgramFilter programFilterToHal(@Nullable ProgramList.Filter filter) {
+ if (filter == null) filter = new ProgramList.Filter();
+
ProgramFilter hwFilter = new ProgramFilter();
filter.getIdentifierTypes().stream().forEachOrdered(hwFilter.identifierTypes::add);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index eee830f..1bc7423 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -56,6 +56,7 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.net.util.VersionedBroadcastListener;
@@ -98,6 +99,7 @@
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import com.android.server.net.BaseNetworkObserver;
@@ -181,9 +183,10 @@
private final VersionedBroadcastListener mCarrierConfigChange;
// TODO: Delete SimChangeListener; it's obsolete.
private final SimChangeListener mSimChange;
+ private final TetheringDependencies mDeps;
private volatile TetheringConfiguration mConfig;
- private String mCurrentUpstreamIface;
+ private InterfaceSet mCurrentUpstreamIfaceSet;
private Notification.Builder mTetheredNotificationBuilder;
private int mLastNotificationId;
@@ -202,12 +205,13 @@
mPolicyManager = policyManager;
mLooper = looper;
mSystemProperties = systemProperties;
+ mDeps = deps;
mPublicSync = new Object();
mTetherStates = new ArrayMap<>();
- mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
+ mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
final Handler smHandler = mTetherMasterSM.getHandler();
@@ -215,8 +219,8 @@
deps.getOffloadHardwareInterface(smHandler, mLog),
mContext.getContentResolver(), mNMService,
mLog);
- mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
- mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
+ mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
+ TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
IntentFilter filter = new IntentFilter();
@@ -1160,12 +1164,11 @@
}
// Needed because the canonical source of upstream truth is just the
- // upstream interface name, |mCurrentUpstreamIface|. This is ripe for
- // future simplification, once the upstream Network is canonical.
+ // upstream interface set, |mCurrentUpstreamIfaceSet|.
private boolean pertainsToCurrentUpstream(NetworkState ns) {
- if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
+ if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
- if (mCurrentUpstreamIface.equals(ifname)) {
+ if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
return true;
}
}
@@ -1241,7 +1244,7 @@
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
- TetherMasterSM(String name, Looper looper) {
+ TetherMasterSM(String name, Looper looper, TetheringDependencies deps) {
super(name, looper);
mInitialState = new InitialState();
@@ -1261,7 +1264,7 @@
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList<>();
- mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+ mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
mOffload = new OffloadWrapper();
setInitialState(mInitialState);
@@ -1360,31 +1363,27 @@
}
protected void setUpstreamNetwork(NetworkState ns) {
- String iface = null;
+ InterfaceSet ifaces = null;
if (ns != null) {
// Find the interface with the default IPv4 route. It may be the
// interface described by linkProperties, or one of the interfaces
// stacked on top of it.
mLog.i("Looking for default routes on: " + ns.linkProperties);
- final String iface4 = getIPv4DefaultRouteInterface(ns);
- final String iface6 = getIPv6DefaultRouteInterface(ns);
- mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6);
-
- iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */;
+ ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns);
+ mLog.i("Found upstream interface(s): " + ifaces);
}
- if (iface != null) {
+ if (ifaces != null) {
setDnsForwarders(ns.network, ns.linkProperties);
}
- notifyDownstreamsOfNewUpstreamIface(iface);
+ notifyDownstreamsOfNewUpstreamIface(ifaces);
if (ns != null && pertainsToCurrentUpstream(ns)) {
// If we already have NetworkState for this network examine
// it immediately, because there likely will be no second
// EVENT_ON_AVAILABLE (it was already received).
handleNewUpstreamNetworkState(ns);
- } else if (mCurrentUpstreamIface == null) {
- // There are no available upstream networks, or none that
- // have an IPv4 default route (current metric for success).
+ } else if (mCurrentUpstreamIfaceSet == null) {
+ // There are no available upstream networks.
handleNewUpstreamNetworkState(null);
}
}
@@ -1411,12 +1410,10 @@
}
}
- protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
- mLog.log("Notifying downstreams of upstream=" + ifaceName);
- mCurrentUpstreamIface = ifaceName;
+ protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
+ mCurrentUpstreamIfaceSet = ifaces;
for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- ifaceName);
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
}
}
@@ -1488,7 +1485,7 @@
// For example, after CONNECTIVITY_ACTION listening is removed, here
// is where we could observe a Wi-Fi network becoming available and
// passing validation.
- if (mCurrentUpstreamIface == null) {
+ if (mCurrentUpstreamIfaceSet == null) {
// If we have no upstream interface, try to run through upstream
// selection again. If, for example, IPv4 connectivity has shown up
// after IPv6 (e.g., 464xlat became available) we want the chance to
@@ -1512,8 +1509,7 @@
handleNewUpstreamNetworkState(ns);
break;
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
- setDnsForwarders(ns.network, ns.linkProperties);
- handleNewUpstreamNetworkState(ns);
+ chooseUpstreamType(false);
break;
case UpstreamNetworkMonitor.EVENT_ON_LOST:
// TODO: Re-evaluate possible upstreams. Currently upstream
@@ -1586,7 +1582,7 @@
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- mCurrentUpstreamIface);
+ mCurrentUpstreamIfaceSet);
// If there has been a change and an upstream is now
// desired, kick off the selection process.
final boolean previousUpstreamWanted = updateUpstreamWanted();
@@ -1864,7 +1860,7 @@
pw.println(" - lastError = " + tetherState.lastError);
}
pw.println("Upstream wanted: " + upstreamWanted());
- pw.println("Current upstream interface: " + mCurrentUpstreamIface);
+ pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
pw.decreaseIndent();
}
@@ -1997,7 +1993,7 @@
final TetherState tetherState = new TetherState(
new TetherInterfaceStateMachine(
iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
- makeControlCallback(iface)));
+ makeControlCallback(iface), mDeps));
mTetherStates.put(iface, tetherState);
tetherState.stateMachine.start();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c9bdcf1..2fda08e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -151,6 +151,13 @@
.multiply(BigInteger.valueOf(howManyPercentIsMost))
.divide(BigInteger.valueOf(100));
}
+ // How many routes to evaluate before bailing and declaring this Vpn should provide
+ // the INTERNET capability. This is necessary because computing the adress space is
+ // O(n²) and this is running in the system service, so a limit is needed to alleviate
+ // the risk of attack.
+ // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
+ // is actually O(n²)+O(n²).
+ private static final int MAX_ROUTES_TO_EVALUATE = 150;
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -862,10 +869,12 @@
*/
@VisibleForTesting
static boolean providesRoutesToMostDestinations(LinkProperties lp) {
+ final List<RouteInfo> routes = lp.getAllRoutes();
+ if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true;
final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
- for (final RouteInfo route : lp.getAllRoutes()) {
+ for (final RouteInfo route : routes) {
IpPrefix destination = route.getDestination();
if (destination.isIPv4()) {
ipv4Prefixes.add(destination);
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 518f6c1..ba67c94 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -30,10 +30,8 @@
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;
@@ -119,7 +117,7 @@
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
- if (!canTetherIPv6(ns, mLog)) {
+ if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
@@ -208,70 +206,6 @@
return null;
}
- private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
- // Broadly speaking:
- //
- // [1] does the upstream have an IPv6 default route?
- //
- // and
- //
- // [2] does the upstream have one or more global IPv6 /64s
- // dedicated to this device?
- //
- // In lieu of Prefix Delegation and other evaluation of whether a
- // prefix may or may not be dedicated to this device, for now just
- // check whether the upstream is TRANSPORT_CELLULAR. This works
- // because "[t]he 3GPP network allocates each default bearer a unique
- // /64 prefix", per RFC 6459, Section 5.2.
-
- final boolean canTether =
- (ns != null) && (ns.network != null) &&
- (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
- // At least one upstream DNS server:
- ns.linkProperties.isProvisioned() &&
- // Minimal amount of IPv6 provisioning:
- ns.linkProperties.hasIPv6DefaultRoute() &&
- ns.linkProperties.hasGlobalIPv6Address() &&
- // Temporary approximation of "dedicated prefix":
- ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
-
- // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
- // tethering with 464xlat involved). TODO: Rectify this shortcoming,
- // likely by calling NetworkManagementService#startInterfaceForwarding()
- // for all upstream interfaces.
- RouteInfo v4default = null;
- RouteInfo v6default = null;
- if (canTether) {
- for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
- if (r.isIPv4Default()) {
- v4default = r;
- } else if (r.isIPv6Default()) {
- v6default = r;
- }
-
- if (v4default != null && v6default != null) {
- break;
- }
- }
- }
-
- final boolean supportedConfiguration =
- (v4default != null) && (v6default != null) &&
- (v4default.getInterface() != null) &&
- v4default.getInterface().equals(v6default.getInterface());
-
- final boolean outcome = canTether && supportedConfiguration;
-
- if (ns == null) {
- sharedLog.log("No available upstream.");
- } else {
- sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
- (outcome ? "available" : "not available"), toDebugString(ns)));
- }
-
- return outcome;
- }
-
private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
final LinkProperties v6only = new LinkProperties();
if (lp == null) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 2224913..5ed14a0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -31,7 +31,7 @@
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
+import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -49,12 +49,12 @@
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Random;
+import java.util.Set;
/**
* Provides the interface to IP-layer serving functionality for a given network
@@ -117,9 +117,11 @@
private final int mInterfaceType;
private final LinkProperties mLinkProperties;
+ private final TetheringDependencies mDeps;
+
private int mLastError;
private int mServingMode;
- private String mMyUpstreamIfaceName; // may change over time
+ private InterfaceSet mUpstreamIfaceSet; // may change over time
private InterfaceParams mInterfaceParams;
// TODO: De-duplicate this with mLinkProperties above. Currently, these link
// properties are those selected by the IPv6TetheringCoordinator and relayed
@@ -134,18 +136,19 @@
public TetherInterfaceStateMachine(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
- IControlsTethering tetherController) {
+ IControlsTethering tetherController,
+ TetheringDependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
- // TODO: This should be passed in for testability.
- mNetd = NetdService.getInstance();
+ mNetd = deps.getNetdService();
mStatsService = statsService;
mTetherController = tetherController;
mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
+ mDeps = deps;
resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mServingMode = IControlsTethering.STATE_AVAILABLE;
@@ -246,16 +249,14 @@
}
private boolean startIPv6() {
- // TODO: Refactor for better testability. This is one of the things
- // that prohibits unittesting IPv6 tethering setup.
- mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+ mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
if (mInterfaceParams == null) {
mLog.e("Failed to find InterfaceParams");
stopIPv6();
return false;
}
- mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
+ mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
if (!mRaDaemon.start()) {
stopIPv6();
return false;
@@ -621,10 +622,10 @@
}
private void cleanupUpstream() {
- if (mMyUpstreamIfaceName == null) return;
+ if (mUpstreamIfaceSet == null) return;
- cleanupUpstreamInterface(mMyUpstreamIfaceName);
- mMyUpstreamIfaceName = null;
+ for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
+ mUpstreamIfaceSet = null;
}
private void cleanupUpstreamInterface(String upstreamIface) {
@@ -660,34 +661,66 @@
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
break;
case CMD_TETHER_CONNECTION_CHANGED:
- String newUpstreamIfaceName = (String)(message.obj);
- if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
- (mMyUpstreamIfaceName != null &&
- mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
+ final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
+ if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
break;
}
- cleanupUpstream();
- if (newUpstreamIfaceName != null) {
+
+ if (newUpstreamIfaceSet == null) {
+ cleanupUpstream();
+ break;
+ }
+
+ for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
+ cleanupUpstreamInterface(removed);
+ }
+
+ final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
+ // This makes the call to cleanupUpstream() in the error
+ // path for any interface neatly cleanup all the interfaces.
+ mUpstreamIfaceSet = newUpstreamIfaceSet;
+
+ for (String ifname : added) {
try {
- mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
- mNMService.startInterfaceForwarding(mIfaceName,
- newUpstreamIfaceName);
+ mNMService.enableNat(mIfaceName, ifname);
+ mNMService.startInterfaceForwarding(mIfaceName, ifname);
} catch (Exception e) {
mLog.e("Exception enabling NAT: " + e);
- cleanupUpstreamInterface(newUpstreamIfaceName);
+ cleanupUpstream();
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
transitionTo(mInitialState);
return true;
}
}
- mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
default:
return false;
}
return true;
}
+
+ private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
+ if (mUpstreamIfaceSet == null && newIfaces == null) return true;
+ if (mUpstreamIfaceSet != null && newIfaces != null) {
+ return mUpstreamIfaceSet.equals(newIfaces);
+ }
+ return false;
+ }
+
+ private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
+ if (mUpstreamIfaceSet == null) return new HashSet<>();
+
+ final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
+ removed.removeAll(newIfaces.ifnames);
+ return removed;
+ }
+
+ private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
+ final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
+ if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
+ return added;
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index b8174b6..66afb0f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,9 +16,18 @@
package com.android.server.connectivity.tethering;
+import android.content.Context;
+import android.net.INetd;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetdService;
import android.os.Handler;
import android.net.util.SharedLog;
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+
/**
* Capture tethering dependencies, for injection.
@@ -29,4 +38,26 @@
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return new OffloadHardwareInterface(h, log);
}
+
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
+ SharedLog log, int what) {
+ return new UpstreamNetworkMonitor(ctx, target, log, what);
+ }
+
+ public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ return new IPv6TetheringCoordinator(notifyList, log);
+ }
+
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+ return new RouterAdvertisementDaemon(ifParams);
+ }
+
+ public InterfaceParams getInterfaceParams(String ifName) {
+ return InterfaceParams.getByName(ifName);
+ }
+
+ public INetd getNetdService() {
+ return NetdService.getInstance();
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
new file mode 100644
index 0000000..6c7ff91
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -0,0 +1,90 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+/**
+ * @hide
+ */
+public final class TetheringInterfaceUtils {
+ /**
+ * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
+ * @return null if there is no usable interface, or a set of at least one interface otherwise.
+ */
+ public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
+ if (ns == null) {
+ return null;
+ }
+
+ final LinkProperties lp = ns.linkProperties;
+ final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
+ final String if6 = getIPv6Interface(ns);
+
+ return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
+ }
+
+ /**
+ * Get the upstream interface for IPv6 tethering.
+ * @return null if there is no usable interface, or the interface name otherwise.
+ */
+ public static @Nullable String getIPv6Interface(NetworkState ns) {
+ // Broadly speaking:
+ //
+ // [1] does the upstream have an IPv6 default route?
+ //
+ // and
+ //
+ // [2] does the upstream have one or more global IPv6 /64s
+ // dedicated to this device?
+ //
+ // In lieu of Prefix Delegation and other evaluation of whether a
+ // prefix may or may not be dedicated to this device, for now just
+ // check whether the upstream is TRANSPORT_CELLULAR. This works
+ // because "[t]he 3GPP network allocates each default bearer a unique
+ // /64 prefix", per RFC 6459, Section 5.2.
+ final boolean canTether =
+ (ns != null) && (ns.network != null) &&
+ (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
+ // At least one upstream DNS server:
+ ns.linkProperties.hasIPv6DnsServer() &&
+ // Minimal amount of IPv6 provisioning:
+ ns.linkProperties.hasGlobalIPv6Address() &&
+ // Temporary approximation of "dedicated prefix":
+ ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+ return canTether
+ ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
+ : null;
+ }
+
+ private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
+ final RouteInfo ri = (lp != null)
+ ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
+ : null;
+ return (ri != null) ? ri.getInterface() : null;
+ }
+}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 63308f8..ec404fe 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -26,6 +26,7 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
import android.content.Context;
import android.content.IContentService;
import android.content.ISyncStatusObserver;
@@ -78,7 +79,7 @@
*/
public final class ContentService extends IContentService.Stub {
static final String TAG = "ContentService";
- static final boolean DEBUG = false;
+ static final boolean DEBUG = true;
public static class Lifecycle extends SystemService {
private ContentService mService;
@@ -451,7 +452,7 @@
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
- uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid));
+ uri.getAuthority(), getSyncExemptionForCaller(uid));
}
}
@@ -508,7 +509,7 @@
int uId = Binder.getCallingUid();
validateExtras(uId, extras);
- final boolean isForegroundSyncRequest = isForegroundSyncRequest(uId, extras);
+ final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(uId, extras);
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
@@ -518,7 +519,7 @@
if (syncManager != null) {
syncManager.scheduleSync(account, userId, uId, authority, extras,
SyncStorageEngine.AuthorityInfo.UNDEFINED,
- /*isAppStandbyExempted=*/ isForegroundSyncRequest);
+ syncExemption);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -561,7 +562,7 @@
final Bundle extras = request.getBundle();
validateExtras(callerUid, extras);
- final boolean isForegroundSyncRequest = isForegroundSyncRequest(callerUid, extras);
+ final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callerUid, extras);
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
@@ -589,7 +590,7 @@
syncManager.scheduleSync(
request.getAccount(), userId, callerUid, request.getProvider(), extras,
SyncStorageEngine.AuthorityInfo.UNDEFINED,
- /*isAppStandbyExempted=*/ isForegroundSyncRequest);
+ syncExemption);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -777,13 +778,15 @@
"no permission to write the sync settings");
enforceCrossUserPermission(userId,
"no permission to modify the sync settings for user " + userId);
+ final int callingUid = Binder.getCallingUid();
+ final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
- providerName, sync);
+ providerName, sync, syncExemptionFlag);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -964,11 +967,14 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ final int callingUid = Binder.getCallingUid();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
+ syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId,
+ getSyncExemptionForCaller(callingUid));
}
} finally {
restoreCallingIdentity(identityToken);
@@ -1263,9 +1269,7 @@
}
private void validateExtras(int callingUid, Bundle extras) {
- if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
- || extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
- ) {
+ if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG)) {
switch (callingUid) {
case Process.ROOT_UID:
case Process.SHELL_UID:
@@ -1277,39 +1281,36 @@
}
}
- private boolean isForegroundSyncRequest(int callingUid, Bundle extras) {
- final boolean isForegroundRequest;
- if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)) {
- isForegroundRequest = true;
- } else if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC)) {
- isForegroundRequest = false;
- } else {
- isForegroundRequest = isUidInForeground(callingUid);
- }
- extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC);
- extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC);
-
- return isForegroundRequest;
+ @SyncExemption
+ private int getSyncExemptionForCaller(int callingUid) {
+ return getSyncExemptionAndCleanUpExtrasForCaller(callingUid, null);
}
- private boolean isUidInForeground(int uid) {
- // If the caller is ADB, we assume it's a background request by default, because
- // that's also the default of requests from the requestsync command.
- // The requestsync command will always set either SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC or
- // SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC (for non-periodic sync requests),
- // so it shouldn't matter in practice.
- switch (uid) {
- case Process.SHELL_UID:
- case Process.ROOT_UID:
- return false;
+ @SyncExemption
+ private int getSyncExemptionAndCleanUpExtrasForCaller(int callingUid, Bundle extras) {
+ if (extras != null) {
+ final int exemption =
+ extras.getInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, -1);
+
+ // Need to remove the virtual extra.
+ extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG);
+ if (exemption != -1) {
+ return exemption;
+ }
}
final ActivityManagerInternal ami =
LocalServices.getService(ActivityManagerInternal.class);
- if (ami != null) {
- return ami.getUidProcessState(uid)
- <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ final int procState = (ami != null)
+ ? ami.getUidProcessState(callingUid)
+ : ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+ if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+ return ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP;
}
- return false;
+ if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ return ContentResolver.SYNC_EXEMPTION_ACTIVE;
+ }
+ return ContentResolver.SYNC_EXEMPTION_NONE;
}
/**
diff --git a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
new file mode 100644
index 0000000..62fb751
--- /dev/null
+++ b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
@@ -0,0 +1,68 @@
+/*
+ * 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.server.content;
+
+import android.app.usage.UsageStatsManagerInternal;
+import android.os.SystemClock;
+import android.util.Pair;
+
+import com.android.server.AppStateTracker;
+import com.android.server.LocalServices;
+
+import java.util.HashMap;
+
+class SyncAdapterStateFetcher {
+
+ private final HashMap<Pair<Integer, String>, Integer> mBucketCache =
+ new HashMap<>();
+
+ public SyncAdapterStateFetcher() {
+ }
+
+ /**
+ * Return sync adapter state with a cache.
+ */
+ public int getStandbyBucket(int userId, String packageName) {
+ final Pair<Integer, String> key = Pair.create(userId, packageName);
+ final Integer cached = mBucketCache.get(key);
+ if (cached != null) {
+ return cached;
+ }
+ final UsageStatsManagerInternal usmi =
+ LocalServices.getService(UsageStatsManagerInternal.class);
+ if (usmi == null) {
+ return -1; // Unknown.
+ }
+
+ final int value = usmi.getAppStandbyBucket(packageName, userId,
+ SystemClock.elapsedRealtime());
+ mBucketCache.put(key, value);
+ return value;
+ }
+
+ /**
+ * Return UID active state.
+ */
+ public boolean isAppActive(int uid) {
+ final AppStateTracker ast =
+ LocalServices.getService(AppStateTracker.class);
+ if (ast == null) {
+ return false;
+ }
+
+ return ast.isUidActive(uid);
+ }
+}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7089268..d1f50b7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -29,9 +29,11 @@
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
import android.content.Context;
import android.content.ISyncAdapter;
import android.content.ISyncAdapterUnsyncableAccountCallback;
@@ -70,6 +72,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -88,6 +91,8 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
+import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleController.LocalService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.job.JobSchedulerInternal;
@@ -550,10 +555,6 @@
return mJobScheduler;
}
- /**
- * Should only be created after {@link ContentService#systemReady()} so that
- * {@link PackageManager} is ready to query.
- */
public SyncManager(Context context, boolean factoryTest) {
// Initialize the SyncStorageEngine first, before registering observers
// and creating threads and so on; it may fail if the disk is full.
@@ -566,9 +567,9 @@
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
@Override
public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras,
- boolean isAppStandbyExempted) {
+ @SyncExemption int syncExemptionFlag) {
scheduleSync(info.account, info.userId, reason, info.provider, extras,
- AuthorityInfo.UNDEFINED, isAppStandbyExempted);
+ AuthorityInfo.UNDEFINED, syncExemptionFlag);
}
});
@@ -599,7 +600,7 @@
scheduleSync(null, UserHandle.USER_ALL,
SyncOperation.REASON_SERVICE_CHANGED,
type.authority, null, AuthorityInfo.UNDEFINED,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
}
}
}, mSyncHandler);
@@ -649,7 +650,7 @@
scheduleSync(account, UserHandle.getUserId(uid),
SyncOperation.REASON_ACCOUNTS_UPDATED,
null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
}
});
@@ -883,9 +884,9 @@
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
String requestedAuthority, Bundle extras, int targetSyncState,
- boolean isAppStandbyExempted) {
+ @SyncExemption int syncExemptionFlag) {
scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
- 0 /* min delay */, true /* checkIfAccountReady */, isAppStandbyExempted);
+ 0 /* min delay */, true /* checkIfAccountReady */, syncExemptionFlag);
}
/**
@@ -894,7 +895,7 @@
private void scheduleSync(Account requestedAccount, int userId, int reason,
String requestedAuthority, Bundle extras, int targetSyncState,
final long minDelayMillis, boolean checkIfAccountReady,
- boolean isAppStandbyExempted) {
+ @SyncExemption int syncExemptionFlag) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -904,7 +905,7 @@
+ requestedAuthority
+ " reason=" + reason
+ " checkIfAccountReady=" + checkIfAccountReady
- + " isAppStandbyExempted=" + isAppStandbyExempted);
+ + " syncExemptionFlag=" + syncExemptionFlag);
}
AccountAndUser[] accounts = null;
@@ -1016,7 +1017,7 @@
scheduleSync(account.account, userId, reason, authority,
finalExtras, targetSyncState, minDelayMillis,
true /* checkIfAccountReady */,
- isAppStandbyExempted);
+ syncExemptionFlag);
}
}
));
@@ -1067,7 +1068,7 @@
sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
() -> scheduleSync(account.account, account.userId, reason,
authority, finalExtras, targetSyncState, minDelayMillis,
- false, isAppStandbyExempted));
+ false, syncExemptionFlag));
} else {
// Initialisation sync.
Bundle newExtras = new Bundle();
@@ -1086,7 +1087,7 @@
new SyncOperation(account.account, account.userId,
owningUid, owningPackage, reason, source,
authority, newExtras, allowParallelSyncs,
- isAppStandbyExempted),
+ syncExemptionFlag),
minDelayMillis
);
}
@@ -1103,7 +1104,7 @@
postScheduleSyncMessage(
new SyncOperation(account.account, account.userId,
owningUid, owningPackage, reason, source,
- authority, extras, allowParallelSyncs, isAppStandbyExempted),
+ authority, extras, allowParallelSyncs, syncExemptionFlag),
minDelayMillis
);
}
@@ -1217,12 +1218,12 @@
* ms to batch syncs.
*/
public void scheduleLocalSync(Account account, int userId, int reason, String authority,
- boolean isAppStandbyExempted) {
+ @SyncExemption int syncExemptionFlag) {
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(account, userId, reason, authority, extras,
AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */,
- isAppStandbyExempted);
+ syncExemptionFlag);
}
public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1493,7 +1494,7 @@
// If any of the duplicate ones has exemption, then we inherit it.
if (!syncOperation.isPeriodic) {
- boolean inheritAppStandbyExemption = false;
+ int inheritedSyncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
// Check currently running syncs
for (ActiveSyncContext asc: mActiveSyncContexts) {
@@ -1534,10 +1535,11 @@
// This means the duplicate one has a negative expected run time, but it hasn't
// been executed possibly because of app-standby.
- if (syncOperation.isAppStandbyExempted
- && (minDelay == 0)
- && !syncToRun.isAppStandbyExempted) {
+ if ((minDelay == 0)
+ && (syncToRun.syncExemptionFlag < syncOperation.syncExemptionFlag)) {
syncToRun = syncOperation;
+ inheritedSyncExemptionFlag =
+ Math.max(inheritedSyncExemptionFlag, syncToRun.syncExemptionFlag);
}
}
@@ -1551,9 +1553,8 @@
if (isLoggable) {
Slog.v(TAG, "Cancelling duplicate sync " + op);
}
- if (op.isAppStandbyExempted) {
- inheritAppStandbyExemption = true;
- }
+ inheritedSyncExemptionFlag =
+ Math.max(inheritedSyncExemptionFlag, op.syncExemptionFlag);
cancelJob(op, "scheduleSyncOperationH-duplicate");
}
}
@@ -1570,8 +1571,9 @@
}
// If any of the duplicates had exemption, we exempt the current one.
- if (inheritAppStandbyExemption) {
- syncOperation.isAppStandbyExempted = true;
+ //
+ if (inheritedSyncExemptionFlag > ContentResolver.SYNC_EXEMPTION_NONE) {
+ syncOperation.syncExemptionFlag = inheritedSyncExemptionFlag;
}
}
@@ -1591,7 +1593,7 @@
// Note this logic means when an exempted sync fails,
// the back-off one will inherit it too, and will be exempted from app-standby.
- final int jobFlags = syncOperation.isAppStandbyExempted
+ final int jobFlags = syncOperation.isAppStandbyExempted()
? JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY : 0;
JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
@@ -1615,6 +1617,19 @@
b.setRequiresCharging(true);
}
+ if (syncOperation.syncExemptionFlag
+ == ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP) {
+ DeviceIdleController.LocalService dic =
+ LocalServices.getService(DeviceIdleController.LocalService.class);
+ if (dic != null) {
+ dic.addPowerSaveTempWhitelistApp(Process.SYSTEM_UID,
+ syncOperation.owningPackage,
+ mConstants.getKeyExemptionTempWhitelistDurationInSeconds() * 1000,
+ UserHandle.getUserId(syncOperation.owningUid),
+ /* sync=*/ false, "sync by top app");
+ }
+ }
+
getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage,
syncOperation.target.userId, syncOperation.wakeLockName());
}
@@ -1736,7 +1751,7 @@
mContext.getOpPackageName());
for (Account account : accounts) {
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
- AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false);
+ AuthorityInfo.NOT_INITIALIZED, ContentResolver.SYNC_EXEMPTION_NONE);
}
}
@@ -1930,7 +1945,10 @@
protected void dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- dumpSyncState(ipw);
+
+ final SyncAdapterStateFetcher buckets = new SyncAdapterStateFetcher();
+
+ dumpSyncState(ipw, buckets);
mConstants.dump(pw, "");
dumpSyncAdapters(ipw);
@@ -1991,7 +2009,7 @@
return ret;
}
- protected void dumpPendingSyncs(PrintWriter pw) {
+ protected void dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
List<SyncOperation> pendingSyncs = getAllPendingSyncs();
pw.print("Pending Syncs: ");
@@ -2001,14 +2019,14 @@
int count = 0;
for (SyncOperation op: pendingSyncs) {
if (!op.isPeriodic) {
- pw.println(op.dump(null, false));
+ pw.println(op.dump(null, false, buckets));
count++;
}
}
pw.println();
}
- protected void dumpPeriodicSyncs(PrintWriter pw) {
+ protected void dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
List<SyncOperation> pendingSyncs = getAllPendingSyncs();
pw.print("Periodic Syncs: ");
@@ -2018,7 +2036,7 @@
int count = 0;
for (SyncOperation op: pendingSyncs) {
if (op.isPeriodic) {
- pw.println(op.dump(null, false));
+ pw.println(op.dump(null, false, buckets));
count++;
}
}
@@ -2075,7 +2093,7 @@
return true;
}
- protected void dumpSyncState(PrintWriter pw) {
+ protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) {
final StringBuilder sb = new StringBuilder();
pw.print("Data connected: "); pw.println(mDataConnectionIsConnected);
@@ -2150,13 +2168,13 @@
sb.setLength(0);
pw.print(formatDurationHMS(sb, durationInSeconds));
pw.print(" - ");
- pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
+ pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets));
pw.println();
}
pw.println();
- dumpPendingSyncs(pw);
- dumpPeriodicSyncs(pw);
+ dumpPendingSyncs(pw, buckets);
+ dumpPeriodicSyncs(pw, buckets);
// Join the installed sync adapter with the accounts list and emit for everything.
pw.println("Sync Status");
@@ -3219,7 +3237,7 @@
scheduleSync(syncTargets.account, syncTargets.userId,
SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
null, AuthorityInfo.NOT_INITIALIZED,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
}
}
@@ -3286,7 +3304,7 @@
syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC, extras,
syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
- pollFrequencyMillis, flexMillis, /*isAppStandbyExempted=*/ false);
+ pollFrequencyMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
final int syncOpState = computeSyncOpState(op);
switch (syncOpState) {
@@ -3431,6 +3449,15 @@
Slog.v(TAG, syncContext.toString());
}
}
+ if (op.isAppStandbyExempted()) {
+ final UsageStatsManagerInternal usmi = LocalServices.getService(
+ UsageStatsManagerInternal.class);
+ if (usmi != null) {
+ usmi.reportExemptedSyncStart(op.owningPackage,
+ UserHandle.getUserId(op.owningUid));
+ }
+ }
+
// Connect to the sync adapter.
int targetUid;
ComponentName targetComponent;
@@ -3604,7 +3631,7 @@
syncOperation.retries++;
if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
- syncOperation.isAppStandbyExempted = false;
+ syncOperation.syncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
}
// the operation failed so increase the backoff time
@@ -3672,7 +3699,7 @@
syncOperation.reason,
syncOperation.syncSource, info.provider, new Bundle(),
syncOperation.allowParallelSyncs,
- syncOperation.isAppStandbyExempted));
+ syncOperation.syncExemptionFlag));
}
}
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 061e4ca..2a5858c 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -52,6 +52,12 @@
private static final int DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION = 5;
private int mMaxRetriesWithAppStandbyExemption = DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION;
+ private static final String KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS =
+ "exemption_temp_whitelist_duration_in_seconds";
+ private static final int DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS = 10 * 60;
+ private int mKeyExemptionTempWhitelistDurationInSeconds
+ = DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS;
+
protected SyncManagerConstants(Context context) {
super(null);
mContext = context;
@@ -97,6 +103,11 @@
mMaxRetriesWithAppStandbyExemption = parser.getInt(
KEY_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION,
DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION);
+
+ mKeyExemptionTempWhitelistDurationInSeconds = parser.getInt(
+ KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS,
+ DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS);
+
}
}
@@ -124,6 +135,12 @@
}
}
+ public int getKeyExemptionTempWhitelistDurationInSeconds() {
+ synchronized (mLock) {
+ return mKeyExemptionTempWhitelistDurationInSeconds;
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
synchronized (mLock) {
pw.print(prefix);
@@ -144,6 +161,10 @@
pw.print(prefix);
pw.print(" mMaxRetriesWithAppStandbyExemption=");
pw.println(mMaxRetriesWithAppStandbyExemption);
+
+ pw.print(prefix);
+ pw.print(" mKeyExemptionTempWhitelistDurationInSeconds=");
+ pw.println(mKeyExemptionTempWhitelistDurationInSeconds);
}
}
}
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 96bdaea..d097563 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -19,6 +19,7 @@
import android.accounts.Account;
import android.app.job.JobInfo;
import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -98,33 +99,33 @@
/** jobId of the JobScheduler job corresponding to this sync */
public int jobId;
- /** Whether this operation should be exempted from the app-standby throttling. */
- public boolean isAppStandbyExempted;
+ @SyncExemption
+ public int syncExemptionFlag;
public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
int reason, int source, String provider, Bundle extras,
- boolean allowParallelSyncs, boolean isAppStandbyExempted) {
+ boolean allowParallelSyncs, @SyncExemption int syncExemptionFlag) {
this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
- reason, source, extras, allowParallelSyncs, isAppStandbyExempted);
+ reason, source, extras, allowParallelSyncs, syncExemptionFlag);
}
private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
int reason, int source, Bundle extras, boolean allowParallelSyncs,
- boolean isAppStandbyExempted) {
+ @SyncExemption int syncExemptionFlag) {
this(info, owningUid, owningPackage, reason, source, extras, allowParallelSyncs, false,
- NO_JOB_ID, 0, 0, isAppStandbyExempted);
+ NO_JOB_ID, 0, 0, syncExemptionFlag);
}
public SyncOperation(SyncOperation op, long periodMillis, long flexMillis) {
this(op.target, op.owningUid, op.owningPackage, op.reason, op.syncSource,
new Bundle(op.extras), op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
- periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
+ periodMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
}
public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
int reason, int source, Bundle extras, boolean allowParallelSyncs,
boolean isPeriodic, int sourcePeriodicId, long periodMillis,
- long flexMillis, boolean isAppStandbyExempted) {
+ long flexMillis, @SyncExemption int syncExemptionFlag) {
this.target = info;
this.owningUid = owningUid;
this.owningPackage = owningPackage;
@@ -138,7 +139,7 @@
this.flexMillis = flexMillis;
this.jobId = NO_JOB_ID;
this.key = toKey();
- this.isAppStandbyExempted = isAppStandbyExempted;
+ this.syncExemptionFlag = syncExemptionFlag;
}
/* Get a one off sync operation instance from a periodic sync. */
@@ -148,7 +149,7 @@
}
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */,
- periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
+ periodMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
return op;
}
@@ -166,7 +167,7 @@
periodMillis = other.periodMillis;
flexMillis = other.flexMillis;
this.key = other.key;
- isAppStandbyExempted = other.isAppStandbyExempted;
+ syncExemptionFlag = other.syncExemptionFlag;
}
/**
@@ -235,7 +236,7 @@
jobInfoExtras.putLong("flexMillis", flexMillis);
jobInfoExtras.putLong("expectedRuntime", expectedRuntime);
jobInfoExtras.putInt("retries", retries);
- jobInfoExtras.putBoolean("isAppStandbyExempted", isAppStandbyExempted);
+ jobInfoExtras.putInt("syncExemptionFlag", syncExemptionFlag);
return jobInfoExtras;
}
@@ -256,7 +257,7 @@
Bundle extras;
boolean allowParallelSyncs, isPeriodic;
long periodMillis, flexMillis;
- boolean isAppStandbyExempted;
+ int syncExemptionFlag;
if (!jobExtras.getBoolean("SyncManagerJob", false)) {
return null;
@@ -275,7 +276,8 @@
initiatedBy = jobExtras.getInt("sourcePeriodicId", NO_JOB_ID);
periodMillis = jobExtras.getLong("periodMillis");
flexMillis = jobExtras.getLong("flexMillis");
- isAppStandbyExempted = jobExtras.getBoolean("isAppStandbyExempted", false);
+ syncExemptionFlag = jobExtras.getInt("syncExemptionFlag",
+ ContentResolver.SYNC_EXEMPTION_NONE);
extras = new Bundle();
PersistableBundle syncExtras = jobExtras.getPersistableBundle("syncExtras");
@@ -298,7 +300,7 @@
new SyncStorageEngine.EndPoint(account, provider, userId);
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, source,
extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis,
- isAppStandbyExempted);
+ syncExemptionFlag);
op.jobId = jobExtras.getInt("jobId");
op.expectedRuntime = jobExtras.getLong("expectedRuntime");
op.retries = jobExtras.getInt("retries");
@@ -361,10 +363,10 @@
@Override
public String toString() {
- return dump(null, true);
+ return dump(null, true, null);
}
- String dump(PackageManager pm, boolean shorter) {
+ String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) {
StringBuilder sb = new StringBuilder();
sb.append("JobId=").append(jobId)
.append(" ")
@@ -385,8 +387,18 @@
if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
sb.append(" EXPEDITED");
}
- if (isAppStandbyExempted) {
- sb.append(" STANDBY-EXEMPTED");
+ switch (syncExemptionFlag) {
+ case ContentResolver.SYNC_EXEMPTION_NONE:
+ break;
+ case ContentResolver.SYNC_EXEMPTION_ACTIVE:
+ sb.append(" STANDBY-EXEMPTED");
+ break;
+ case ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP:
+ sb.append(" STANDBY-EXEMPTED(TOP)");
+ break;
+ default:
+ sb.append(" ExemptionFlag=" + syncExemptionFlag);
+ break;
}
sb.append(" Reason=");
sb.append(reasonToString(pm, reason));
@@ -397,21 +409,31 @@
SyncManager.formatDurationHMS(sb, flexMillis);
sb.append(")");
}
+ if (retries > 0) {
+ sb.append(" Retries=");
+ sb.append(retries);
+ }
if (!shorter) {
sb.append(" Owner={");
UserHandle.formatUid(sb, owningUid);
sb.append(" ");
sb.append(owningPackage);
+ if (appStates != null) {
+ sb.append(" [");
+ sb.append(appStates.getStandbyBucket(
+ UserHandle.getUserId(owningUid), owningPackage));
+ sb.append("]");
+
+ if (appStates.isAppActive(owningUid)) {
+ sb.append(" [ACTIVE]");
+ }
+ }
sb.append("}");
if (!extras.keySet().isEmpty()) {
sb.append(" ");
extrasToStringBuilder(extras, sb);
}
}
- if (retries > 0) {
- sb.append(" Retries=");
- sb.append(retries);
- }
return sb.toString();
}
@@ -464,6 +486,10 @@
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
}
+ boolean isAppStandbyExempted() {
+ return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE;
+ }
+
static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
if (bundle == null) {
sb.append("null");
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 8b67b7a..6081af8 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -22,6 +22,7 @@
import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
import android.content.Context;
import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
@@ -341,7 +342,7 @@
/** Called when a sync is needed on an account(s) due to some change in state. */
public void onSyncRequest(EndPoint info, int reason, Bundle extras,
- boolean exemptFromAppStandby);
+ @SyncExemption int syncExemptionFlag);
}
interface PeriodicSyncAddedListener {
@@ -647,7 +648,7 @@
}
public void setSyncAutomatically(Account account, int userId, String providerName,
- boolean sync) {
+ boolean sync, @SyncExemption int syncExemptionFlag) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
+ ", user " + userId + " -> " + sync);
@@ -677,7 +678,7 @@
if (sync) {
requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName,
new Bundle(),
- /* exemptFromAppStandby=*/ false);
+ syncExemptionFlag);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
queueBackup();
@@ -739,7 +740,7 @@
}
if (syncable == AuthorityInfo.SYNCABLE) {
requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
- /*exemptFromAppStandby=*/ false); // Or the caller FG state?
+ ContentResolver.SYNC_EXEMPTION_NONE);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
@@ -900,7 +901,8 @@
return true;
}
- public void setMasterSyncAutomatically(boolean flag, int userId) {
+ public void setMasterSyncAutomatically(boolean flag, int userId,
+ @SyncExemption int syncExemptionFlag) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
if (auto != null && auto.equals(flag)) {
@@ -912,7 +914,7 @@
if (flag) {
requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
new Bundle(),
- /*exemptFromAppStandby=*/ false); // Or the caller FG state?
+ syncExemptionFlag);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
@@ -2046,7 +2048,8 @@
String value = c.getString(c.getColumnIndex("value"));
if (name == null) continue;
if (name.equals("listen_for_tickles")) {
- setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0);
+ setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0,
+ ContentResolver.SYNC_EXEMPTION_NONE);
} else if (name.startsWith("sync_provider_")) {
String provider = name.substring("sync_provider_".length(),
name.length());
@@ -2143,11 +2146,11 @@
}
private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras,
- boolean exemptFromAppStandby) {
+ @SyncExemption int syncExemptionFlag) {
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
&& mSyncRequestListener != null) {
mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras,
- exemptFromAppStandby);
+ syncExemptionFlag);
} else {
SyncRequest.Builder req =
new SyncRequest.Builder()
@@ -2159,7 +2162,7 @@
}
private void requestSync(Account account, int userId, int reason, String authority,
- Bundle extras, boolean exemptFromAppStandby) {
+ Bundle extras, @SyncExemption int syncExemptionFlag) {
// If this is happening in the system process, then call the syncrequest listener
// to make a request back to the SyncManager directly.
// If this is probably a test instance, then call back through the ContentResolver
@@ -2168,7 +2171,7 @@
&& mSyncRequestListener != null) {
mSyncRequestListener.onSyncRequest(
new EndPoint(account, authority, userId),
- reason, extras, exemptFromAppStandby);
+ reason, extras, syncExemptionFlag);
} else {
ContentResolver.requestSync(account, authority, extras);
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4b2b5e..c7ae1f4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -695,6 +695,27 @@
}
}
+ private void setSaturationLevelInternal(float level) {
+ if (level < 0 || level > 1) {
+ throw new IllegalArgumentException("Saturation level must be between 0 and 1");
+ }
+ float[] matrix = (level == 1.0f ? null : computeSaturationMatrix(level));
+ DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
+ dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION, matrix);
+ }
+
+ private static float[] computeSaturationMatrix(float saturation) {
+ float desaturation = 1.0f - saturation;
+ float[] luminance = {0.231f * desaturation, 0.715f * desaturation, 0.072f * desaturation};
+ float[] matrix = {
+ luminance[0] + saturation, luminance[0], luminance[0], 0,
+ luminance[1], luminance[1] + saturation, luminance[1], 0,
+ luminance[2], luminance[2], luminance[2] + saturation, 0,
+ 0, 0, 0, 1
+ };
+ return matrix;
+ }
+
private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
IMediaProjection projection, int callingUid, String packageName, String name, int width,
int height, int densityDpi, Surface surface, int flags, String uniqueId) {
@@ -1687,6 +1708,19 @@
}
@Override // Binder call
+ public void setSaturationLevel(float level) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_SATURATION,
+ "Permission required to set display saturation level");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setSaturationLevelInternal(level);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public int createVirtualDisplay(IVirtualDisplayCallback callback,
IMediaProjection projection, String packageName, String name,
int width, int height, int densityDpi, Surface surface, int flags,
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 000fcf3..a94f049 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -17,7 +17,6 @@
package com.android.server.display;
import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.opengl.Matrix;
import android.os.IBinder;
import android.os.Parcel;
@@ -28,7 +27,6 @@
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-
import com.android.internal.app.ColorDisplayController;
import java.util.Arrays;
@@ -46,6 +44,10 @@
*/
public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
/**
+ * Color transform level used to adjust the color saturation of the display.
+ */
+ public static final int LEVEL_COLOR_MATRIX_SATURATION = 150;
+ /**
* Color transform level used by A11y services to make the display monochromatic.
*/
public static final int LEVEL_COLOR_MATRIX_GRAYSCALE = 200;
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 1dbcfd6..40d2a3a 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 @@
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 @@
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 @@
}
mScreenOn = true;
//cancel the alarm
- mAlarm.cancel(mIdleTriggerIntent);
+ mAlarm.cancel(mIdleAlarmListener);
if (mIdle) {
// possible transition to not-idle
mIdle = false;
@@ -169,20 +169,24 @@
}
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 3374b30..5955c9c 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 @@
* 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.
@@ -2576,9 +2578,9 @@
// register for connectivity change events, this is equivalent to the deprecated way of
// registering for CONNECTIVITY_ACTION broadcasts
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
+ networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
NetworkRequest networkRequest = networkRequestBuilder.build();
mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
@@ -2899,6 +2901,8 @@
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 f617964..4b58d53 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 @@
}
@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/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 050c1f4..b0afac2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -79,6 +79,7 @@
private final PlatformKeyManager mPlatformKeyManager;
private final RecoverySnapshotStorage mRecoverySnapshotStorage;
private final RecoverySnapshotListenersStorage mSnapshotListenersStorage;
+ private final TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
public static KeySyncTask newInstance(
Context context,
@@ -98,7 +99,8 @@
credentialType,
credential,
credentialUpdated,
- PlatformKeyManager.getInstance(context, recoverableKeyStoreDb));
+ PlatformKeyManager.getInstance(context, recoverableKeyStoreDb),
+ new TestOnlyInsecureCertificateHelper());
}
/**
@@ -110,6 +112,7 @@
* @param credential The credential, encoded as a {@link String}.
* @param credentialUpdated signals weather credentials were updated.
* @param platformKeyManager platform key manager
+ * @param TestOnlyInsecureCertificateHelper utility class used for end-to-end tests
*/
@VisibleForTesting
KeySyncTask(
@@ -120,7 +123,8 @@
int credentialType,
String credential,
boolean credentialUpdated,
- PlatformKeyManager platformKeyManager) {
+ PlatformKeyManager platformKeyManager,
+ TestOnlyInsecureCertificateHelper TestOnlyInsecureCertificateHelper) {
mSnapshotListenersStorage = recoverySnapshotListenersStorage;
mRecoverableKeyStoreDb = recoverableKeyStoreDb;
mUserId = userId;
@@ -129,6 +133,7 @@
mCredentialUpdated = credentialUpdated;
mPlatformKeyManager = platformKeyManager;
mRecoverySnapshotStorage = snapshotStorage;
+ mTestOnlyInsecureCertificateHelper = TestOnlyInsecureCertificateHelper;
}
@Override
@@ -189,8 +194,9 @@
PublicKey publicKey;
String rootCertAlias =
mRecoverableKeyStoreDb.getActiveRootOfTrust(mUserId, recoveryAgentUid);
+ rootCertAlias = mTestOnlyInsecureCertificateHelper
+ .getDefaultCertificateAliasIfEmpty(rootCertAlias);
- rootCertAlias = replaceEmptyValueWithSecureDefault(rootCertAlias);
CertPath certPath = mRecoverableKeyStoreDb.getRecoveryServiceCertPath(mUserId,
recoveryAgentUid, rootCertAlias);
if (certPath != null) {
@@ -212,12 +218,18 @@
return;
}
- // The only place in this class which uses credential value
- if (!TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS.equals(
- rootCertAlias)) {
- // TODO: allow only whitelisted LSKF usage
- Log.w(TAG, "Untrusted root certificate is used by recovery agent "
+ if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(rootCertAlias)) {
+ Log.w(TAG, "Insecure root certificate is used by recovery agent "
+ recoveryAgentUid);
+ if (mTestOnlyInsecureCertificateHelper.doesCredentailSupportInsecureMode(
+ mCredentialType, mCredential)) {
+ Log.w(TAG, "Whitelisted credential is used to generate snapshot by "
+ + "recovery agent "+ recoveryAgentUid);
+ } else {
+ Log.w(TAG, "Non whitelisted credential is used to generate recovery snapshot by "
+ + recoveryAgentUid + " - ignore attempt.");
+ return; // User secret will not be used.
+ }
}
byte[] salt = generateSalt();
@@ -239,8 +251,10 @@
return;
}
- // TODO: filter raw keys based on the root of trust.
- // It is the only place in the class where raw key material is used.
+ // Only include insecure key material for test
+ if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(rootCertAlias)) {
+ rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+ }
SecretKey recoveryKey;
try {
recoveryKey = generateRecoveryKey();
@@ -467,14 +481,4 @@
}
return keyEntries;
}
-
- private @NonNull String replaceEmptyValueWithSecureDefault(
- @Nullable String rootCertificateAlias) {
- if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
- Log.e(TAG, "rootCertificateAlias is null or empty");
- // Use the default Google Key Vault Service CA certificate if the alias is not provided
- rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
- }
- return rootCertificateAlias;
- }
}
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 30125f8..ff4c678 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
@@ -39,7 +38,6 @@
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.RecoveryController;
-import android.security.keystore.recovery.TrustedRootCertificates;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.KeyStore;
import android.util.ArrayMap;
@@ -100,8 +98,8 @@
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
private final PlatformKeyManager mPlatformKeyManager;
- private final KeyStore mKeyStore;
private final ApplicationKeyStorage mApplicationKeyStorage;
+ private final TestOnlyInsecureCertificateHelper mTestCertHelper;
/**
* Returns a new or existing instance.
@@ -126,14 +124,14 @@
mInstance = new RecoverableKeyStoreManager(
context.getApplicationContext(),
- keystore,
db,
new RecoverySessionStorage(),
Executors.newSingleThreadExecutor(),
- new RecoverySnapshotStorage(),
+ RecoverySnapshotStorage.newInstance(),
new RecoverySnapshotListenersStorage(),
platformKeyManager,
- applicationKeyStorage);
+ applicationKeyStorage,
+ new TestOnlyInsecureCertificateHelper());
}
return mInstance;
}
@@ -141,16 +139,15 @@
@VisibleForTesting
RecoverableKeyStoreManager(
Context context,
- KeyStore keystore,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
ExecutorService executorService,
RecoverySnapshotStorage snapshotStorage,
RecoverySnapshotListenersStorage listenersStorage,
PlatformKeyManager platformKeyManager,
- ApplicationKeyStorage applicationKeyStorage) {
+ ApplicationKeyStorage applicationKeyStorage,
+ TestOnlyInsecureCertificateHelper TestOnlyInsecureCertificateHelper) {
mContext = context;
- mKeyStore = keystore;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
@@ -158,6 +155,7 @@
mSnapshotStorage = snapshotStorage;
mPlatformKeyManager = platformKeyManager;
mApplicationKeyStorage = applicationKeyStorage;
+ mTestCertHelper = TestOnlyInsecureCertificateHelper;
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
@@ -176,7 +174,8 @@
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
- rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
+ rootCertificateAlias
+ = mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
// Always set active alias to the argument of the last call to initRecoveryService method,
// even if cert file is incorrect.
@@ -221,7 +220,8 @@
// Randomly choose and validate an endpoint certificate from the list
CertPath certPath;
- X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
+ X509Certificate rootCert =
+ mTestCertHelper.getRootCertificate(rootCertificateAlias);
try {
Log.d(TAG, "Getting and validating a random endpoint certificate");
certPath = certXml.getRandomEndpointCert(rootCert);
@@ -270,7 +270,8 @@
@NonNull byte[] recoveryServiceSigFile)
throws RemoteException {
checkRecoverKeyStorePermission();
- rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
+ rootCertificateAlias =
+ mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
@@ -284,7 +285,8 @@
ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file.");
}
- X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
+ X509Certificate rootCert =
+ mTestCertHelper.getRootCertificate(rootCertificateAlias);
try {
sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile);
} catch (CertValidationException e) {
@@ -524,7 +526,8 @@
@NonNull List<KeyChainProtectionParams> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
- rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
+ rootCertificateAlias =
+ mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
Preconditions.checkNotNull(sessionId, "invalid session");
Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
Preconditions.checkNotNull(vaultParams, "vaultParams is null");
@@ -539,7 +542,8 @@
}
try {
- CertUtils.validateCertPath(getRootCertificate(rootCertificateAlias), certPath);
+ CertUtils.validateCertPath(
+ mTestCertHelper.getRootCertificate(rootCertificateAlias), certPath);
} catch (CertValidationException e) {
Log.e(TAG, "Failed to validate the given cert path", e);
throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
@@ -668,40 +672,6 @@
}
/**
- * 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 {
@@ -965,27 +935,6 @@
}
}
- private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException {
- rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
- X509Certificate rootCertificate =
- TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
- if (rootCertificate == null) {
- throw new ServiceSpecificException(
- ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
- }
- return rootCertificate;
- }
-
- private @NonNull String replaceEmptyValueWithSecureDefault(
- @Nullable String rootCertificateAlias) {
- if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
- Log.e(TAG, "rootCertificateAlias is null or empty");
- // Use the default Google Key Vault Service CA certificate if the alias is not provided
- rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
- }
- return rootCertificateAlias;
- }
-
private void checkRecoverKeyStorePermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.RECOVER_KEYSTORE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
new file mode 100644
index 0000000..490f733
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -0,0 +1,103 @@
+/*
+ * 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.server.locksettings.recoverablekeystore;
+
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
+
+import com.android.internal.widget.LockPatternUtils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keystore.recovery.TrustedRootCertificates;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import javax.crypto.SecretKey;
+
+/**
+ * The class provides helper methods to support end-to-end test with insecure certificate.
+ */
+public class TestOnlyInsecureCertificateHelper {
+ private static final String TAG = "TestCertHelper";
+
+ /**
+ * Constructor for the helper class.
+ */
+ public TestOnlyInsecureCertificateHelper() {
+ }
+
+ /**
+ * Returns a root certificate installed in the system for given alias.
+ * Returns default secure certificate if alias is empty or null.
+ * Can return insecure certificate for its alias.
+ */
+ public @NonNull X509Certificate
+ getRootCertificate(String rootCertificateAlias) throws RemoteException {
+ rootCertificateAlias = getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
+ if (isTestOnlyCertificate(rootCertificateAlias)) {
+ return TrustedRootCertificates.getTestOnlyInsecureCertificate();
+ }
+
+ X509Certificate rootCertificate =
+ TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
+ if (rootCertificate == null) {
+ throw new ServiceSpecificException(
+ ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
+ }
+ return rootCertificate;
+ }
+
+ public @NonNull String getDefaultCertificateAliasIfEmpty(
+ @Nullable String rootCertificateAlias) {
+ if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
+ Log.e(TAG, "rootCertificateAlias is null or empty - use secure default value");
+ // Use the default Google Key Vault Service CA certificate if the alias is not provided
+ rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
+ }
+ return rootCertificateAlias;
+ }
+
+ public boolean isTestOnlyCertificate(String rootCertificateAlias) {
+ return TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS
+ .equals(rootCertificateAlias);
+ }
+
+ public boolean doesCredentailSupportInsecureMode(int credentialType, String credential) {
+ return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD)
+ && (credential != null)
+ && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX);
+ }
+
+ public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) {
+ if (rawKeys == null) {
+ return null;
+ }
+ Map<String, SecretKey> filteredKeys = new HashMap<>();
+ for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) {
+ String alias = entry.getKey();
+ if (alias != null
+ && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) {
+ filteredKeys.put(entry.getKey(), entry.getValue());
+ Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot");
+ }
+ }
+ return filteredKeys;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
index dcaa0b4..f789155 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -23,6 +23,9 @@
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
@@ -128,6 +131,11 @@
}
break;
+ case TAG_BACKEND_PUBLIC_KEY:
+ builder.setTrustedHardwarePublicKey(
+ readBlobTag(parser, TAG_BACKEND_PUBLIC_KEY));
+ break;
+
case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST:
builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser));
break;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
index ee8b2cf..ff30ecd 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -35,6 +35,7 @@
static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial";
static final String TAG_SERVER_PARAMS = "serverParams";
static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath";
+ static final String TAG_BACKEND_PUBLIC_KEY = "backendPublicKey";
static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST =
"keyChainProtectionParamsList";
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
index f817a8f..17a16bf 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -24,6 +24,9 @@
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
@@ -159,6 +162,10 @@
writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams());
writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH,
keyChainSnapshot.getTrustedHardwareCertPath());
+ if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) {
+ writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY,
+ keyChainSnapshot.getTrustedHardwarePublicKey());
+ }
}
private static void writePropertyTag(
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 3f93cc6..c02b103 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,13 +17,28 @@
package com.android.server.locksettings.recoverablekeystore.storage;
import android.annotation.Nullable;
+import android.os.Environment;
import android.security.keystore.recovery.KeyChainSnapshot;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotDeserializer;
+import com.android.server.locksettings.recoverablekeystore.serialization
+ .KeyChainSnapshotParserException;
+import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.util.Locale;
/**
- * In-memory storage for recovery snapshots.
+ * Storage for recovery snapshots. Stores snapshots in memory, backed by disk storage.
*
* <p>Recovery snapshots are generated after a successful screen unlock. They are only generated if
* the recoverable keystore has been mutated since the previous snapshot. This class stores only the
@@ -33,14 +48,46 @@
* {@link com.android.server.locksettings.recoverablekeystore.KeySyncTask} thread.
*/
public class RecoverySnapshotStorage {
+
+ private static final String TAG = "RecoverySnapshotStorage";
+
+ private static final String ROOT_PATH = "system";
+ private static final String STORAGE_PATH = "recoverablekeystore/snapshots/";
+
@GuardedBy("this")
private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>();
+ private final File rootDirectory;
+
+ /**
+ * A new instance, storing snapshots in /data/system/recoverablekeystore/snapshots.
+ *
+ * <p>NOTE: calling this multiple times DOES NOT return the same instance, so will NOT be backed
+ * by the same in-memory store.
+ */
+ public static RecoverySnapshotStorage newInstance() {
+ return new RecoverySnapshotStorage(
+ new File(Environment.getDataDirectory(), ROOT_PATH));
+ }
+
+ @VisibleForTesting
+ public RecoverySnapshotStorage(File rootDirectory) {
+ this.rootDirectory = rootDirectory;
+ }
+
/**
* Sets the latest {@code snapshot} for the recovery agent {@code uid}.
*/
public synchronized void put(int uid, KeyChainSnapshot snapshot) {
mSnapshotByUid.put(uid, snapshot);
+
+ try {
+ writeToDisk(uid, snapshot);
+ } catch (IOException | CertificateEncodingException e) {
+ Log.e(TAG,
+ String.format(Locale.US, "Error persisting snapshot for %d to disk", uid),
+ e);
+ }
}
/**
@@ -48,7 +95,17 @@
*/
@Nullable
public synchronized KeyChainSnapshot get(int uid) {
- return mSnapshotByUid.get(uid);
+ KeyChainSnapshot snapshot = mSnapshotByUid.get(uid);
+ if (snapshot != null) {
+ return snapshot;
+ }
+
+ try {
+ return readFromDisk(uid);
+ } catch (IOException | KeyChainSnapshotParserException e) {
+ Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e);
+ return null;
+ }
}
/**
@@ -56,5 +113,66 @@
*/
public synchronized void remove(int uid) {
mSnapshotByUid.remove(uid);
+ getSnapshotFile(uid).delete();
+ }
+
+ /**
+ * Writes the snapshot for recovery agent {@code uid} to disk.
+ *
+ * @throws IOException if an IO error occurs writing to disk.
+ */
+ private void writeToDisk(int uid, KeyChainSnapshot snapshot)
+ throws IOException, CertificateEncodingException {
+ File snapshotFile = getSnapshotFile(uid);
+
+ try (
+ FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)
+ ) {
+ KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream);
+ } catch (IOException | CertificateEncodingException e) {
+ // If we fail to write the latest snapshot, we should delete any older snapshot that
+ // happens to be around. Otherwise snapshot syncs might end up going 'back in time'.
+ snapshotFile.delete();
+ throw e;
+ }
+ }
+
+ /**
+ * Reads the last snapshot for recovery agent {@code uid} from disk.
+ *
+ * @return The snapshot, or null if none existed.
+ * @throws IOException if an IO error occurs reading from disk.
+ */
+ @Nullable
+ private KeyChainSnapshot readFromDisk(int uid)
+ throws IOException, KeyChainSnapshotParserException {
+ File snapshotFile = getSnapshotFile(uid);
+
+ try (
+ FileInputStream fileInputStream = new FileInputStream(snapshotFile)
+ ) {
+ return KeyChainSnapshotDeserializer.deserialize(fileInputStream);
+ } catch (IOException | KeyChainSnapshotParserException e) {
+ // If we fail to read the latest snapshot, we should delete it in case it is in some way
+ // corrupted. We can regenerate snapshots anyway.
+ snapshotFile.delete();
+ throw e;
+ }
+ }
+
+ private File getSnapshotFile(int uid) {
+ File folder = getStorageFolder();
+ String fileName = getSnapshotFileName(uid);
+ return new File(folder, fileName);
+ }
+
+ private String getSnapshotFileName(int uid) {
+ return String.format(Locale.US, "%d.xml", uid);
+ }
+
+ private File getStorageFolder() {
+ File folder = new File(rootDirectory, STORAGE_PATH);
+ folder.mkdirs();
+ return folder;
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7842d5..d5a32aa 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 @@
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 210857e..f31ca0a 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.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 @@
@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());
}
@@ -2149,7 +2152,8 @@
final NotificationChannel channel = channels.get(i);
Preconditions.checkNotNull(channel, "channel in list is null");
mRankingHelper.createNotificationChannel(pkg, uid, channel,
- true /* fromTargetApp */);
+ true /* fromTargetApp */, mConditionProviders.isPackageOrComponentAllowed(
+ pkg, UserHandle.getUserId(uid)));
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(uid),
mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
@@ -4691,11 +4695,12 @@
private boolean playSound(final NotificationRecord record, Uri soundUri) {
boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
- // do not play notifications if there is a user of exclusive audio focus
- // or the device is in vibrate mode
- if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
- != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
- AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
+ // play notifications if there is no user of exclusive audio focus
+ // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+ // VIBRATE ringer mode)
+ if (!mAudioManager.isAudioFocusExclusive()
+ && (mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1b0bf2..af64683 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -38,7 +38,7 @@
ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped);
void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
- boolean fromTargetApp);
+ boolean fromTargetApp, boolean hasDndAccess);
void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f163113..98d5c9a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -569,7 +569,7 @@
@Override
public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
- boolean fromTargetApp) {
+ boolean fromTargetApp, boolean hasDndAccess) {
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(channel);
Preconditions.checkNotNull(channel.getId());
@@ -610,8 +610,9 @@
existing.setImportance(channel.getImportance());
}
- // system apps can bypass dnd if the user hasn't changed any fields on the channel yet
- if (existing.getUserLockedFields() == 0 & isSystemApp) {
+ // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+ // fields on the channel yet
+ if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
existing.setBypassDnd(channel.canBypassDnd());
}
@@ -624,7 +625,7 @@
}
// Reset fields that apps aren't allowed to set.
- if (fromTargetApp && !isSystemApp) {
+ if (fromTargetApp && !(isSystemApp || hasDndAccess)) {
channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
}
if (fromTargetApp) {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index c0c66b2..6cf8f86 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -468,12 +468,14 @@
private final LinkedList<String> mPendingLookups;
private final Context mContext;
+ // Amount of time to wait for a result from the contacts db before rechecking affinity.
+ private static final long LOOKUP_TIME = 1000;
private float mContactAffinity = NONE;
private NotificationRecord mRecord;
private PeopleRankingReconsideration(Context context, String key,
LinkedList<String> pendingLookups) {
- super(key);
+ super(key, LOOKUP_TIME);
mContext = context;
mPendingLookups = pendingLookups;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 24abf86..9d3f48b 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 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 61c6be7..9807342 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -449,13 +449,15 @@
pw.increaseIndent();
for (String isa : dexCodeInstructionSets) {
- String status = null;
try {
- status = DexFile.getDexFileStatus(path, isa);
+ String[] status = DexFile.getDexFileOptimizationStatus(path, isa);
+ String compilationStatus = status[0];
+ String compilationReason = status[1];
+ pw.println(isa + ": [status=" + compilationStatus
+ +"] reason=[" + compilationReason + "]");
} catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
+ pw.println(isa + ": [Exception]: " + ioe.getMessage());
}
- pw.println(isa + ": " + status);
}
if (useInfo.isUsedByOtherApps(path)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e08ec556..cb1972f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -808,7 +808,7 @@
}
final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
- String targetPath, Object installLock) {
+ String targetPath) {
if (overlayPackages == null || overlayPackages.isEmpty()) {
return null;
}
@@ -828,20 +828,9 @@
//
// OverlayManagerService will update each of them with a correct gid from its
// target package app id.
- if (installLock != null) {
- synchronized (installLock) {
- mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
- UserHandle.getSharedAppGid(
- UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
- }
- } else {
- // We can call mInstaller without holding mInstallLock because mInstallLock
- // is held before running parallel parsing.
- // Moreover holding mInstallLock on each parsing thread causes dead-lock.
- mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
- UserHandle.getSharedAppGid(
- UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
- }
+ mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
+ UserHandle.getSharedAppGid(
+ UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
if (overlayPathList == null) {
overlayPathList = new ArrayList<String>();
}
@@ -856,13 +845,15 @@
String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
List<PackageParser.Package> overlayPackages;
- synchronized (mPackages) {
- overlayPackages = getStaticOverlayPackages(
- mPackages.values(), targetPackageName);
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ overlayPackages = getStaticOverlayPackages(
+ mPackages.values(), targetPackageName);
+ }
+ // It is safe to keep overlayPackages without holding mPackages because static overlay
+ // packages can't be uninstalled or disabled.
+ return getStaticOverlayPaths(overlayPackages, targetPath);
}
- // It is safe to keep overlayPackages without holding mPackages because static overlay
- // packages can't be uninstalled or disabled.
- return getStaticOverlayPaths(overlayPackages, targetPath, mInstallLock);
}
@Override public final String[] getOverlayApks(String targetPackageName) {
@@ -895,11 +886,13 @@
synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
// We can trust mOverlayPackages without holding mPackages because package uninstall
// can't happen while running parallel parsing.
- // Moreover holding mPackages on each parsing thread causes dead-lock.
+ // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
+ // because mInstallLock is held before running parallel parsing.
+ // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
return mOverlayPackages == null ? null :
getStaticOverlayPaths(
getStaticOverlayPackages(mOverlayPackages, targetPackageName),
- targetPath, null);
+ targetPath);
}
}
@@ -16810,12 +16803,6 @@
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
- } else {
- for (int currentUserId : sUserManager.getUserIds()) {
- ps.setInstalled(true, currentUserId);
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
- installerPackageName);
- }
}
// When replacing an existing package, preserve the original install reason for all
@@ -17335,8 +17322,11 @@
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2e6e348..fd02347 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1330,6 +1330,9 @@
mHandler.post(mHiddenNavPanic);
}
+ // Abort possibly stuck animations.
+ mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
+
// Latch power key state to detect screenshot chord.
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
@@ -4362,6 +4365,9 @@
* given the situation with the keyguard.
*/
void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
+ // Abort possibly stuck animations.
+ mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
+
if (respectKeyguard) {
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
@@ -7175,7 +7181,7 @@
}
@Override
- public int rotationForOrientationLw(int orientation, int lastRotation) {
+ public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
if (false) {
Slog.v(TAG, "rotationForOrientationLw(orient="
+ orientation + ", last=" + lastRotation
@@ -7196,7 +7202,11 @@
}
final int preferredRotation;
- if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
+ if (!defaultDisplay) {
+ // For secondary displays we ignore things like displays sensors, docking mode and
+ // rotation lock, and always prefer a default rotation.
+ preferredRotation = Surface.ROTATION_0;
+ } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
// Ignore sensor when lid switch is open and rotation is forced.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ec0521d..0a6ae4e 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -407,7 +407,10 @@
/**
* Returns true if this window has been shown on screen at some time in
* the past. Must be called with the window manager lock held.
+ *
+ * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods.
*/
+ @Deprecated
public boolean hasDrawnLw();
/**
@@ -649,6 +652,12 @@
return Integer.toString(lens);
}
}
+
+ /**
+ * Hint to window manager that the user has started a navigation action that should
+ * abort animations that have no timeout, in case they got stuck.
+ */
+ void triggerAnimationFailsafe();
}
/** Window has been added to the screen. */
@@ -1426,10 +1435,13 @@
* @param orientation An orientation constant, such as
* {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
* @param lastRotation The most recently used rotation.
+ * @param defaultDisplay Flag indicating whether the rotation is computed for the default
+ * display. Currently for all non-default displays sensors, docking mode,
+ * rotation lock and other factors are ignored.
* @return The surface rotation to use.
*/
public int rotationForOrientationLw(@ActivityInfo.ScreenOrientation int orientation,
- int lastRotation);
+ int lastRotation, boolean defaultDisplay);
/**
* Given an orientation constant and a rotation, returns true if the rotation
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 941cd44..e56caf8 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 @@
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 @@
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 @@
@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 @@
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 d2d0e60..180f343 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.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 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
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 @@
mShm = null;
return tmp;
}
+
+ public int getBufferLimit() {
+ return mBuffer == null ? -1 : mBuffer.limit();
+ }
}
}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 0b7d9d0..fd0b6f1 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -168,7 +168,8 @@
}
@Override
- public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
+ public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token)
+ throws RemoteException {
verifyCaller(pkg);
enforceAccess(pkg, uri);
int user = Binder.getCallingUserHandle().getIdentifier();
@@ -210,7 +211,8 @@
}
@Override
- public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+ public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+ String[] autoGrantPermissions) throws RemoteException {
if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
== PERMISSION_GRANTED) {
return SliceManager.PERMISSION_GRANTED;
@@ -218,6 +220,11 @@
if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
return SliceManager.PERMISSION_GRANTED;
}
+ for (String perm : autoGrantPermissions) {
+ if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
+ return SliceManager.PERMISSION_USER_GRANTED;
+ }
+ }
synchronized (mLock) {
if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
return SliceManager.PERMISSION_USER_GRANTED;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index b3d28fc..753394d 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.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.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 @@
* 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 @@
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 @@
informAllUidsLocked(context);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
- forgetEverything();
+ forgetEverythingLocked();
}
}
}
@@ -466,34 +481,32 @@
}
@Override // Binder call
- public void setPullingAlarms(long timestampMs, long intervalMs) {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
- // only fire when it awakens.
- // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
- // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
- mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, timestampMs, intervalMs,
- mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void setPullingAlarm(long nextPullTimeMs) {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG,
+ "Setting pulling alarm in about " + (nextPullTimeMs - SystemClock.elapsedRealtime()));
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+ // only fire when it awakens.
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
@Override // Binder call
- public void cancelPullingAlarms() {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Cancelling pulling alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void cancelPullingAlarm() {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling pulling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
private void addNetworkStats(
@@ -753,7 +766,7 @@
});
}
- private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+ private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
if (mWifiManager == null) {
mWifiManager =
@@ -921,8 +934,8 @@
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: {
@@ -1057,7 +1070,7 @@
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);
@@ -1089,7 +1102,7 @@
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();
}
}
}
@@ -1098,18 +1111,60 @@
@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();
- cancelPullingAlarms();
+ 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/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 56c9e51..f19c554 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1485,7 +1485,7 @@
if (w == null || winHint != null && w != winHint) {
return;
}
- final boolean surfaceReady = w.hasDrawnLw() // Regular case
+ final boolean surfaceReady = w.isDrawnLw() // Regular case
|| w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2ffdbfd..c9ff9e3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -909,6 +909,7 @@
return mRotation;
}
+ @VisibleForTesting
void setRotation(int newRotation) {
mRotation = newRotation;
}
@@ -974,7 +975,11 @@
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
- int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
+ final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
+ isDefaultDisplay);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
+ + mDisplayId + " based on lastOrientation=" + lastOrientation
+ + " and oldRotation=" + oldRotation);
boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
rotation);
@@ -1010,7 +1015,8 @@
final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
lastOrientation, rotation);
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + lastOrientation
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " selected orientation " + lastOrientation
+ ", got rotation " + rotation + " which has "
+ (altOrientation ? "incompatible" : "compatible") + " metrics");
@@ -1019,7 +1025,8 @@
return false;
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " rotation changed to " + rotation
+ (altOrientation ? " (alt)" : "") + " from " + oldRotation
+ (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
@@ -1647,8 +1654,8 @@
if (mService.mDisplayFrozen) {
if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Display is frozen, return " + mLastWindowForcedOrientation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " is frozen, return " + mLastWindowForcedOrientation);
// If the display is frozen, some activities may be in the middle of restarting, and
// thus have removed their old window. If the window has the flag to hide the lock
// screen, then the lock screen can re-appear and inflict its own orientation on us.
@@ -1660,8 +1667,8 @@
// window. We don't want to check the show when locked window directly though as
// things aren't stable while the display is frozen, for example the window could be
// momentarily unavailable due to activity relaunch.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
- + "return " + mLastOrientation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ + " is frozen while keyguard locked, return " + mLastOrientation);
return mLastOrientation;
}
} else {
@@ -3503,19 +3510,22 @@
// In a car, you cannot physically rotate the screen, so it doesn't make sense to
// allow anything but the default orientation.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation);
+ "Forcing UNSPECIFIED orientation in car for display id=" + mDisplayId
+ + ". Ignoring " + orientation);
return SCREEN_ORIENTATION_UNSPECIFIED;
}
if (orientation != SCREEN_ORIENTATION_UNSET
&& orientation != SCREEN_ORIENTATION_BEHIND) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "App is requesting an orientation, return " + orientation);
+ "App is requesting an orientation, return " + orientation
+ + " for display id=" + mDisplayId);
return orientation;
}
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No app is requesting an orientation, return " + mLastOrientation);
+ "No app is requesting an orientation, return " + mLastOrientation
+ + " for display id=" + mDisplayId);
// The next app has not been requested to be visible, so we keep the current orientation
// to prevent freezing/unfreezing the display too early.
return mLastOrientation;
@@ -3708,7 +3718,8 @@
return SCREEN_ORIENTATION_UNSET;
}
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req
+ + " for display id=" + mDisplayId);
return (mLastWindowForcedOrientation = req);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 1018848..7274aee 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -34,6 +34,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -61,15 +62,17 @@
* window manager when the animation is completed. In addition, window manager may also notify the
* app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
*/
-public class RecentsAnimationController {
+public class RecentsAnimationController implements DeathRecipient {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
private static final boolean DEBUG = false;
+ private static final long FAILSAFE_DELAY = 1000;
private final WindowManagerService mService;
private final IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
private final int mDisplayId;
+ private final Runnable mFailsafeRunnable = this::cancelAnimation;
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mHomeAppToken;
@@ -223,6 +226,13 @@
return;
}
+ try {
+ mRunner.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ cancelAnimation();
+ return;
+ }
+
// Adjust the wallpaper visibility for the showing home activity
final AppWindowToken recentsComponentAppToken =
dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
@@ -296,6 +306,7 @@
// We've already canceled the animation
return;
}
+ mService.mH.removeCallbacks(mFailsafeRunnable);
mCanceled = true;
try {
mRunner.onAnimationCanceled();
@@ -321,10 +332,21 @@
}
mPendingAnimations.clear();
+ mRunner.asBinder().unlinkToDeath(this, 0);
+
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
}
+ void scheduleFailsafe() {
+ mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
+ }
+
+ @Override
+ public void binderDied() {
+ cancelAnimation();
+ }
+
void checkAnimationReady(WallpaperController wallpaperController) {
if (mPendingStart) {
final boolean wallpaperReady = !isHomeAppOverWallpaper()
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 379a1a1..3be7b235 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
@@ -47,7 +48,7 @@
/**
* Helper class to run app animations in a remote process.
*/
-class RemoteAnimationController {
+class RemoteAnimationController implements DeathRecipient {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
private static final long TIMEOUT_MS = 2000;
@@ -56,12 +57,10 @@
private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
- private FinishedCallback mFinishedCallback;
+ private final Runnable mTimeoutRunnable = this::cancelAnimation;
- private final Runnable mTimeoutRunnable = () -> {
- onAnimationFinished();
- invokeAnimationCancelled();
- };
+ private FinishedCallback mFinishedCallback;
+ private boolean mCanceled;
RemoteAnimationController(WindowManagerService service,
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
@@ -90,7 +89,7 @@
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo() {
- if (mPendingAnimations.isEmpty()) {
+ if (mPendingAnimations.isEmpty() || mCanceled) {
onAnimationFinished();
return;
}
@@ -107,8 +106,8 @@
}
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
- mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
- mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -120,6 +119,17 @@
}
}
+ private void cancelAnimation() {
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ return;
+ }
+ mCanceled = true;
+ }
+ onAnimationFinished();
+ invokeAnimationCancelled();
+ }
+
private void writeStartDebugStatement() {
Slog.i(TAG, "Starting remote animation");
final StringWriter sw = new StringWriter();
@@ -154,6 +164,7 @@
private void onAnimationFinished() {
mHandler.removeCallbacks(mTimeoutRunnable);
+ mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
synchronized (mService.mWindowMap) {
releaseFinishedCallback();
mService.openSurfaceTransaction();
@@ -193,6 +204,11 @@
mService.sendSetRunningRemoteAnimation(pid, running);
}
+ @Override
+ public void binderDied() {
+ cancelAnimation();
+ }
+
private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
RemoteAnimationController mOuter;
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index f51a6a9..e97b366 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -79,17 +79,25 @@
}
// Top
- c.clipRect(new Rect(0, 0, dw, mThickness), Region.Op.REPLACE);
+ c.save();
+ c.clipRect(new Rect(0, 0, dw, mThickness));
c.drawColor(Color.RED);
+ c.restore();
// Left
- c.clipRect(new Rect(0, 0, mThickness, dh), Region.Op.REPLACE);
+ c.save();
+ c.clipRect(new Rect(0, 0, mThickness, dh));
c.drawColor(Color.RED);
+ c.restore();
// Right
- c.clipRect(new Rect(dw - mThickness, 0, dw, dh), Region.Op.REPLACE);
+ c.save();
+ c.clipRect(new Rect(dw - mThickness, 0, dw, dh));
c.drawColor(Color.RED);
+ c.restore();
// Bottom
- c.clipRect(new Rect(0, dh - mThickness, dw, dh), Region.Op.REPLACE);
+ c.save();
+ c.clipRect(new Rect(0, dh - mThickness, dw, dh));
c.drawColor(Color.RED);
+ c.restore();
mSurface.unlockCanvasAndPost(c);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6bceda5..6686b80 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -419,4 +419,10 @@
* @see android.view.IWindowManager#lockNow
*/
public abstract void lockNow();
+
+ /**
+ * Return the user that owns the given window, {@link android.os.UserHandle#USER_NULL} if
+ * the window token is not found.
+ */
+ public abstract int getWindowOwnerUserId(IBinder windowToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b5c006..f1cd46b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -25,12 +25,10 @@
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-import static android.content.Intent.EXTRA_USER_HANDLE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.os.UserHandle.USER_NULL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DOCKED_INVALID;
@@ -181,7 +179,6 @@
import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
@@ -2423,8 +2420,8 @@
final int oldRotation = defaultDisplayContent.getRotation();
final boolean oldAltOrientation = defaultDisplayContent.getAltOrientation();
- final int rotation = mPolicy.rotationForOrientationLw(lastOrientation,
- oldRotation);
+ final int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
+ true /* defaultDisplay */);
boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
lastOrientation, rotation);
if (oldRotation == rotation && oldAltOrientation == altOrientation) {
@@ -2793,6 +2790,11 @@
mTaskSnapshotController.screenTurningOff(listener);
}
+ @Override
+ public void triggerAnimationFailsafe() {
+ mH.sendEmptyMessage(H.ANIMATION_FAILSAFE);
+ }
+
/**
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
* performance, only one layout pass should be done. This can be called multiple times, and
@@ -4566,6 +4568,7 @@
public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57;
public static final int SET_HAS_OVERLAY_UI = 58;
public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
+ public static final int ANIMATION_FAILSAFE = 60;
/**
* Used to denote that an integer field in a message will not be used.
@@ -4984,6 +4987,14 @@
mAmInternal.setRunningRemoteAnimation(msg.arg1, msg.arg2 == 1);
}
break;
+ case ANIMATION_FAILSAFE: {
+ synchronized (mWindowMap) {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.scheduleFailsafe();
+ }
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -7351,6 +7362,17 @@
public void lockNow() {
WindowManagerService.this.lockNow(null);
}
+
+ @Override
+ public int getWindowOwnerUserId(IBinder token) {
+ synchronized (mWindowMap) {
+ WindowState window = mWindowMap.get(token);
+ if (window != null) {
+ return UserHandle.getUserId(window.mOwnerUid);
+ }
+ return UserHandle.USER_NULL;
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
index ecf1a33..f7ca363 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -231,6 +231,13 @@
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/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 847222a..61b48c2 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -380,6 +380,7 @@
auto jProduct = make_javastr(env, prop10.product);
auto jVersion = make_javastr(env, prop10.version);
auto jSerial = make_javastr(env, prop10.serial);
+ constexpr bool isInitializationRequired = true;
bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
auto jVendorInfo = prop11 ? VendorInfoFromHal(env, prop11->vendorInfo) : nullptr;
@@ -394,9 +395,9 @@
return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz,
gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
- prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
- jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr,
- jVendorInfo.get()));
+ prop10.numAudioSources, isInitializationRequired, prop10.supportsCapture, jBands.get(),
+ isBgScanSupported, jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(),
+ nullptr, jVendorInfo.get()));
}
JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -712,7 +713,7 @@
gjni.ModuleProperties.clazz = MakeGlobalRefOrDie(env, modulePropertiesClass);
gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
"(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
- "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
+ "Ljava/lang/String;IIZZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
"[I[ILjava/util/Map;Ljava/util/Map;)V");
auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index dcee151..21fea1c 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 @@
}
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 @@
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 @@
{"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 71c2ea1..1c9782f 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 @@
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 5cfba22..90e8a9c 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 @@
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 @@
// 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 @@
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 @@
} 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 @@
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 @@
return new LockPatternUtils(mContext);
}
- PasswordBlacklist newPasswordBlacklist(File file) {
- return new PasswordBlacklist(file);
- }
-
boolean storageManagerIsFileBasedEncryptionEnabled() {
return StorageManager.isFileEncryptedNativeOnly();
}
@@ -2028,8 +2011,9 @@
Settings.Global.putString(mContext.getContentResolver(), name, value);
}
- void settingsSystemPutString(String name, String value) {
- Settings.System.putString(mContext.getContentResolver(), name, value);
+ void settingsSystemPutStringForUser(String name, String value, int userId) {
+ Settings.System.putStringForUser(
+ mContext.getContentResolver(), name, value, userId);
}
void securityLogSetLoggingEnabledProperty(boolean enabled) {
@@ -4412,136 +4396,6 @@
}
}
- /* @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) {
@@ -4937,11 +4791,6 @@
return false;
}
}
-
- if (isPasswordBlacklistedInternal(userHandle, password)) {
- Slog.w(LOG_TAG, "resetPassword: the password is blacklisted");
- return false;
- }
}
DevicePolicyData policy = getUserData(userHandle);
@@ -5533,10 +5382,11 @@
.setAttestationChallenge(null)
.build();
- final boolean generationResult = keyChain.generateKeyPair(algorithm,
+ final int generationResult = keyChain.generateKeyPair(algorithm,
new ParcelableKeyGenParameterSpec(noAttestationSpec));
- if (!generationResult) {
- Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+ if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
+ Log.e(LOG_TAG, String.format(
+ "KeyChain failed to generate a keypair, error %d.", generationResult));
return false;
}
@@ -5549,12 +5399,17 @@
final byte[] attestationChallenge = keySpec.getAttestationChallenge();
if (attestationChallenge != null) {
- final boolean attestationResult = keyChain.attestKey(
+ final int attestationResult = keyChain.attestKey(
alias, attestationChallenge, attestationUtilsFlags, attestationChain);
- if (!attestationResult) {
+ if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
Log.e(LOG_TAG, String.format(
- "Attestation for %s failed, deleting key.", alias));
+ "Attestation for %s failed (rc=%d), deleting key.",
+ alias, attestationResult));
keyChain.removeKeyPair(alias);
+ if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
+ throw new UnsupportedOperationException(
+ "Device does not support Device ID attestation.");
+ }
return false;
}
}
@@ -10043,15 +9898,17 @@
Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
synchronized (this) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
if (!SYSTEM_SETTINGS_WHITELIST.contains(setting)) {
throw new SecurityException(String.format(
"Permission denial: device owners cannot update %1$s", setting));
}
- mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsSystemPutString(
- setting, value));
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.settingsSystemPutStringForUser(setting, value, callingUserId));
}
}
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 a17a107..0000000
--- 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/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index d41fbce..ca8f727 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -367,7 +367,7 @@
*/
public boolean setApfVersion(int version) {
// This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
- return version == 2;
+ return version >= 2;
}
private void addInstruction(Instruction instruction) {
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/services/net/java/android/net/util/InterfaceSet.java
new file mode 100644
index 0000000..9f26fa1
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceSet.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.net.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+/**
+ * @hide
+ */
+public class InterfaceSet {
+ public final Set<String> ifnames;
+
+ public InterfaceSet(String... names) {
+ final Set<String> nameSet = new HashSet<>();
+ for (String name : names) {
+ if (name != null) nameSet.add(name);
+ }
+ ifnames = Collections.unmodifiableSet(nameSet);
+ }
+
+ @Override
+ public String toString() {
+ final StringJoiner sj = new StringJoiner(",", "[", "]");
+ for (String ifname : ifnames) sj.add(ifname);
+ return sj.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null
+ && obj instanceof InterfaceSet
+ && ifnames.equals(((InterfaceSet)obj).ifnames);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index f603a09..fa41220 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -147,6 +147,15 @@
Looper backupLooper = startBackupThreadAndGetLooper();
mShadowBackupLooper = shadowOf(backupLooper);
+
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ BackupAgentTimeoutParameters agentTimeoutParameters =
+ new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+ agentTimeoutParameters.start();
+
+ // We need to mock BMS timeout parameters before initializing the BackupHandler since
+ // the constructor of BackupHandler relies on the timeout parameters.
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
mBackupManager = spy(FakeIBackupManager.class);
@@ -157,7 +166,8 @@
mTransportManager,
packageManager,
mBackupHandler,
- mWakeLock);
+ mWakeLock,
+ agentTimeoutParameters);
when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 03792b1..92d6bbd 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -41,12 +41,14 @@
import android.app.backup.RestoreSet;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.internal.BackupHandler;
@@ -115,6 +117,15 @@
Looper backupLooper = startBackupThreadAndGetLooper();
mShadowBackupLooper = shadowOf(backupLooper);
+
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ BackupAgentTimeoutParameters agentTimeoutParameters =
+ new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+ agentTimeoutParameters.start();
+
+ // We need to mock BMS timeout parameters before initializing the BackupHandler since
+ // the constructor of BackupHandler relies on it.
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper);
mWakeLock = createBackupWakeLock(application);
@@ -125,7 +136,8 @@
mTransportManager,
application.getPackageManager(),
backupHandler,
- mWakeLock);
+ mWakeLock,
+ agentTimeoutParameters);
when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>());
}
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index c210fde..5a886e3 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -28,6 +28,7 @@
import android.os.PowerManager;
import android.util.SparseArray;
+import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.internal.BackupHandler;
@@ -43,7 +44,8 @@
TransportManager transportManager,
PackageManager packageManager,
BackupHandler backupHandler,
- PowerManager.WakeLock wakeLock) {
+ PowerManager.WakeLock wakeLock,
+ BackupAgentTimeoutParameters agentTimeoutParameters) {
when(backupManagerService.getContext()).thenReturn(context);
when(backupManagerService.getTransportManager()).thenReturn(transportManager);
when(backupManagerService.getPackageManager()).thenReturn(packageManager);
@@ -53,6 +55,7 @@
when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
when(backupManagerService.getWakelock()).thenReturn(wakeLock);
+ when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
}
public static PowerManager.WakeLock createBackupWakeLock(Application application) {
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 082827c..0ec16b5 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
<configuration description="Runs Frameworks Services Tests.">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksServicesTests.apk" />
<option name="test-file-name" value="JobTestApp.apk" />
@@ -22,7 +24,6 @@
<option name="test-file-name" value="SuspendTestApp.apk" />
</target_preparer>
- <option name="test-suite-tag" value="apct" />
<option name="test-tag" value="FrameworksServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 7c3ea4f..e37ed79 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -17,6 +17,7 @@
package com.android.server.content;
import android.accounts.Account;
+import android.content.ContentResolver;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.test.AndroidTestCase;
@@ -60,7 +61,7 @@
"authority1",
b1,
false,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
// Same as op1 but different time infos
SyncOperation op2 = new SyncOperation(account1, 0,
@@ -69,7 +70,7 @@
"authority1",
b1,
false,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
// Same as op1 but different authority
SyncOperation op3 = new SyncOperation(account1, 0,
@@ -78,7 +79,7 @@
"authority2",
b1,
false,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
// Same as op1 but different account
SyncOperation op4 = new SyncOperation(account2, 0,
@@ -87,7 +88,7 @@
"authority1",
b1,
false,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
// Same as op1 but different bundle
SyncOperation op5 = new SyncOperation(account1, 0,
@@ -96,7 +97,7 @@
"authority1",
b2,
false,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
assertEquals(op1.key, op2.key);
assertNotSame(op1.key, op3.key);
@@ -117,7 +118,7 @@
"authority1",
b1,
false,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
PersistableBundle pb = op1.toJobInfoExtras();
SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
@@ -145,7 +146,7 @@
Bundle extras = new Bundle();
SyncOperation periodic = new SyncOperation(ep, 0, "package", 0, 0, extras, false, true,
SyncOperation.NO_JOB_ID, 60000, 10000,
- /*isAppStandbyExempted=*/ false);
+ ContentResolver.SYNC_EXEMPTION_NONE);
SyncOperation oneoff = periodic.createOneTimeSyncOperation();
assertFalse("Conversion to oneoff sync failed.", oneoff.isPeriodic);
assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
deleted file mode 100644
index 7209c79..0000000
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright (C) 2007 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.content;
-
-import android.accounts.Account;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-
-import com.android.internal.os.AtomicFile;
-
-import java.io.File;
-import java.io.FileOutputStream;
-
-/**
- * Test for SyncStorageEngine.
- *
- * bit FrameworksServicesTests:com.android.server.content.SyncStorageEngineTest
- *
- * TODO Broken. Fix it. b/62485315
- */
-@Suppress
-public class SyncStorageEngineTest extends AndroidTestCase {
-
- protected Account account1;
- protected Account account2;
- protected ComponentName syncService1;
- protected String authority1 = "testprovider";
- protected Bundle defaultBundle;
- protected final int DEFAULT_USER = 0;
-
- /* Some default poll frequencies. */
- final long dayPoll = (60 * 60 * 24);
- final long dayFuzz = 60;
- final long thousandSecs = 1000;
- final long thousandSecsFuzz = 100;
-
- MockContentResolver mockResolver;
- SyncStorageEngine engine;
-
- private File getSyncDir() {
- return new File(new File(getContext().getFilesDir(), "system"), "sync");
- }
-
- @Override
- public void setUp() {
- account1 = new Account("a@example.com", "example.type");
- account2 = new Account("b@example.com", "example.type");
- syncService1 = new ComponentName("com.example", "SyncService");
- // Default bundle.
- defaultBundle = new Bundle();
- defaultBundle.putInt("int_key", 0);
- defaultBundle.putString("string_key", "hello");
- // Set up storage engine.
- mockResolver = new MockContentResolver();
- engine = SyncStorageEngine.newTestInstance(
- new TestContext(mockResolver, getContext()));
- }
-
- /**
- * Test that we handle the case of a history row being old enough to purge before the
- * corresponding sync is finished. This can happen if the clock changes while we are syncing.
- *
- */
- // TODO: this test causes AidlTest to fail. Omit for now
- // @SmallTest
- public void testPurgeActiveSync() throws Exception {
- final Account account = new Account("a@example.com", "example.type");
- final String authority = "testprovider";
-
- MockContentResolver mockResolver = new MockContentResolver();
-
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
- new TestContext(mockResolver, getContext()));
- long time0 = 1000;
- SyncOperation op = new SyncOperation(account, 0, 0, "foo",
- SyncOperation.REASON_PERIODIC,
- SyncStorageEngine.SOURCE_LOCAL,
- authority,
- Bundle.EMPTY, true,
- /*isAppStandbyExempted=*/ false);
- long historyId = engine.insertStartSyncEvent(op, time0);
- long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
- engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
- }
-
- @LargeTest
- public void testAuthorityPersistence() throws Exception {
- final Account account1 = new Account("a@example.com", "example.type");
- final Account account2 = new Account("b@example.com", "example.type.2");
- final String authority1 = "testprovider1";
- final String authority2 = "testprovider2";
-
- engine.setMasterSyncAutomatically(false, 0);
-
- engine.setIsSyncable(account1, 0, authority1, 1);
- engine.setSyncAutomatically(account1, 0, authority1, true);
-
- engine.setIsSyncable(account2, 0, authority1, 1);
- engine.setSyncAutomatically(account2, 0, authority1, true);
-
- engine.setIsSyncable(account1, 0, authority2, 1);
- engine.setSyncAutomatically(account1, 0, authority2, false);
-
- engine.setIsSyncable(account2, 0, authority2, 0);
- engine.setSyncAutomatically(account2, 0, authority2, true);
-
- engine.writeAllState();
- engine.clearAndReadState();
-
- assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1));
- assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1));
- assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2));
- assertEquals(true, engine.getSyncAutomatically(account2, 0, authority2));
-
- assertEquals(1, engine.getIsSyncable(account1, 0, authority1));
- assertEquals(1, engine.getIsSyncable(account2, 0, authority1));
- assertEquals(1, engine.getIsSyncable(account1, 0, authority2));
- assertEquals(0, engine.getIsSyncable(account2, 0, authority2));
- }
-
- @MediumTest
- public void testListenForTicklesParsing() throws Exception {
- byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts>\n"
- + "<listenForTickles user=\"0\" enabled=\"false\" />"
- + "<listenForTickles user=\"1\" enabled=\"true\" />"
- + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
- + "<authority id=\"1\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
- + "</accounts>\n").getBytes();
-
- MockContentResolver mockResolver = new MockContentResolver();
- final TestContext testContext = new TestContext(mockResolver, getContext());
-
- File syncDir = getSyncDir();
- syncDir.mkdirs();
- AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- FileOutputStream fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
- assertEquals(false, engine.getMasterSyncAutomatically(0));
- assertEquals(true, engine.getMasterSyncAutomatically(1));
- assertEquals(true, engine.getMasterSyncAutomatically(2));
-
- }
-
- @MediumTest
- public void testAuthorityRenaming() throws Exception {
- final Account account1 = new Account("acc1", "type1");
- final Account account2 = new Account("acc2", "type2");
- final String authorityContacts = "contacts";
- final String authorityCalendar = "calendar";
- final String authorityOther = "other";
- final String authorityContactsNew = "com.android.contacts";
- final String authorityCalendarNew = "com.android.calendar";
-
- MockContentResolver mockResolver = new MockContentResolver();
-
- final TestContext testContext = new TestContext(mockResolver, getContext());
-
- byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts>\n"
- + "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n"
- + "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n"
- + "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n"
- + "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n"
- + "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n"
- + "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n"
- + "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\""
- + " authority=\"com.android.calendar\" />\n"
- + "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\""
- + " authority=\"com.android.contacts\" />\n"
- + "</accounts>\n").getBytes();
-
- File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
- syncDir.mkdirs();
- AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- FileOutputStream fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
- assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityContacts));
- assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityCalendar));
- assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityOther));
- assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityContactsNew));
- assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityCalendarNew));
-
- assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContacts));
- assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendar));
- assertEquals(true, engine.getSyncAutomatically(account2, 0, authorityOther));
- assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContactsNew));
- assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendarNew));
- }
-
- @SmallTest
- public void testSyncableMigration() throws Exception {
- final Account account = new Account("acc", "type");
-
- MockContentResolver mockResolver = new MockContentResolver();
-
- final TestContext testContext = new TestContext(mockResolver, getContext());
-
- byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts>\n"
- + "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n"
- + "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n"
- + "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\""
- + " authority=\"other3\" />\n"
- + "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\""
- + " authority=\"other4\" />\n"
- + "</accounts>\n").getBytes();
-
- File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
- syncDir.mkdirs();
- AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- FileOutputStream fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
- assertEquals(-1, engine.getIsSyncable(account, 0, "other1"));
- assertEquals(1, engine.getIsSyncable(account, 0, "other2"));
- assertEquals(0, engine.getIsSyncable(account, 0, "other3"));
- assertEquals(1, engine.getIsSyncable(account, 0, "other4"));
- }
-
- /**
- * Verify that the API cannot cause a run-time reboot by passing in the empty string as an
- * authority. The problem here is that
- * {@link SyncStorageEngine#getOrCreateAuthorityLocked(account, provider)} would register
- * an empty authority which causes a RTE in {@link SyncManager#scheduleReadyPeriodicSyncs()}.
- * This is not strictly a SSE test, but it does depend on the SSE data structures.
- */
- @SmallTest
- public void testExpectedIllegalArguments() throws Exception {
- try {
- ContentResolver.setSyncAutomatically(account1, "", true);
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.addPeriodicSync(account1, "", Bundle.EMPTY, 84000L);
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.removePeriodicSync(account1, "", Bundle.EMPTY);
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.cancelSync(account1, "");
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.setIsSyncable(account1, "", 0);
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.cancelSync(account1, "");
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.requestSync(account1, "", Bundle.EMPTY);
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- try {
- ContentResolver.getSyncStatus(account1, "");
- fail("empty provider string should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
-
- // Make sure we aren't blocking null account/provider for those functions that use it
- // to specify ALL accounts/providers.
- ContentResolver.requestSync(null, null, Bundle.EMPTY);
- ContentResolver.cancelSync(null, null);
- }
-}
-
-class TestContext extends ContextWrapper {
-
- ContentResolver mResolver;
-
- private final Context mRealContext;
-
- public TestContext(ContentResolver resolver, Context realContext) {
- super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
- mRealContext = realContext;
- mResolver = resolver;
- }
-
- @Override
- public Resources getResources() {
- return mRealContext.getResources();
- }
-
- @Override
- public File getFilesDir() {
- return mRealContext.getFilesDir();
- }
-
- @Override
- public void enforceCallingOrSelfPermission(String permission, String message) {
- }
-
- @Override
- public void sendBroadcast(Intent intent) {
- }
-
- @Override
- public ContentResolver getContentResolver() {
- return mResolver;
- }
-}
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 ab0bfefb..cd39285 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 @@
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 @@
}
@Override
- PasswordBlacklist newPasswordBlacklist(File file) {
- return services.passwordBlacklist;
- }
-
- @Override
boolean storageManagerIsFileBasedEncryptionEnabled() {
return services.storageManager.isFileBasedEncryptionEnabled();
}
@@ -393,8 +382,8 @@
}
@Override
- void settingsSystemPutString(String name, String value) {
- services.settings.settingsSystemPutString(name, value);
+ void settingsSystemPutStringForUser(String name, String value, int userId) {
+ services.settings.settingsSystemPutStringForUser(name, value, userId);
}
@Override
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 fe47de6..b76064b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3454,18 +3454,19 @@
dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS_FOR_VR, "0"));
}
- public void testSetSystemSettingFailWithPO() throws Exception {
- setupProfileOwner();
- assertExpectException(SecurityException.class, null, () ->
- dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0"));
- }
-
- public void testSetSystemSetting() throws Exception {
+ public void testSetSystemSettingWithDO() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0");
- verify(getServices().settings).settingsSystemPutString(
- Settings.System.SCREEN_BRIGHTNESS, "0");
+ verify(getServices().settings).settingsSystemPutStringForUser(
+ Settings.System.SCREEN_BRIGHTNESS, "0", UserHandle.USER_SYSTEM);
+ }
+
+ public void testSetSystemSettingWithPO() throws Exception {
+ setupProfileOwner();
+ dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0");
+ verify(getServices().settings).settingsSystemPutStringForUser(
+ Settings.System.SCREEN_BRIGHTNESS, "0", DpmMockContext.CALLER_USER_HANDLE);
}
public void testSetTime() throws Exception {
@@ -4191,36 +4192,6 @@
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 34c69f5..e753df1 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 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 @@
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);
@@ -419,7 +417,7 @@
public void settingsGlobalPutString(String name, String value) {
}
- public void settingsSystemPutString(String name, String value) {
+ public void settingsSystemPutStringForUser(String name, String value, int callingUserId) {
}
public int settingsGlobalGetInt(String name, int value) {
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 1b3fc2c..0000000
--- 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/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 9ae45ea..b8d2c3e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -33,23 +33,28 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.FileUtils;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -59,6 +64,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.io.File;
import java.nio.charset.StandardCharsets;
@@ -72,6 +78,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeySyncTaskTest {
+
+ private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
private static final String KEY_ALGORITHM = "AES";
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
@@ -91,6 +100,7 @@
@Mock private PlatformKeyManager mPlatformKeyManager;
@Mock private RecoverySnapshotListenersStorage mSnapshotListenersStorage;
+ @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
private RecoverySnapshotStorage mRecoverySnapshotStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -117,7 +127,7 @@
TEST_ROOT_CERT_ALIAS);
mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
TEST_ROOT_CERT_ALIAS);
- mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+ mRecoverySnapshotStorage = new RecoverySnapshotStorage(context.getFilesDir());
mKeySyncTask = new KeySyncTask(
mRecoverableKeyStoreDb,
@@ -127,7 +137,8 @@
TEST_CREDENTIAL_TYPE,
TEST_CREDENTIAL,
/*credentialUpdated=*/ false,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper);
mWrappingKey = generateAndroidKeyStoreKey();
mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
@@ -139,6 +150,10 @@
public void tearDown() {
mRecoverableKeyStoreDb.close();
mDatabaseFile.delete();
+
+ File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
+ SNAPSHOT_TOP_LEVEL_DIRECTORY);
+ FileUtils.deleteContentsAndDir(file);
}
@Test
@@ -277,6 +292,100 @@
}
@Test
+ public void run_InTestModeWithWhitelistedCredentials() throws Exception {
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+
+ // Enter test mode with whitelisted credentials
+ when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(any())).thenReturn(true);
+ when(mTestOnlyInsecureCertificateHelper.doesCredentailSupportInsecureMode(anyInt(), any()))
+ .thenReturn(true);
+ mKeySyncTask.run();
+
+ verify(mTestOnlyInsecureCertificateHelper)
+ .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
+
+ // run whitelist checks
+ verify(mTestOnlyInsecureCertificateHelper)
+ .doesCredentailSupportInsecureMode(anyInt(), any());
+ verify(mTestOnlyInsecureCertificateHelper)
+ .keepOnlyWhitelistedInsecureKeys(any());
+
+ KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ assertNotNull(keyChainSnapshot); // created snapshot
+ List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
+ assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included
+ }
+
+ @Test
+ public void run_InTestModeWithNonWhitelistedCredentials() throws Exception {
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+
+ // Enter test mode with non whitelisted credentials
+ when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(any())).thenReturn(true);
+ when(mTestOnlyInsecureCertificateHelper.doesCredentailSupportInsecureMode(anyInt(), any()))
+ .thenReturn(false);
+ mKeySyncTask.run();
+
+ assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); // not created
+ verify(mTestOnlyInsecureCertificateHelper)
+ .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
+ verify(mTestOnlyInsecureCertificateHelper)
+ .doesCredentailSupportInsecureMode(anyInt(), any());
+ }
+
+ @Test
+ public void run_doesNotFilterCredentialsAndAliasesInProd() throws Exception {
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+
+ mKeySyncTask.run();
+ assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
+
+ verify(mTestOnlyInsecureCertificateHelper)
+ .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .isTestOnlyCertificate(eq(TEST_ROOT_CERT_ALIAS));
+
+ // no whitelists check
+ verify(mTestOnlyInsecureCertificateHelper, never())
+ .doesCredentailSupportInsecureMode(anyInt(), any());
+ verify(mTestOnlyInsecureCertificateHelper, never())
+ .keepOnlyWhitelistedInsecureKeys(any());
+ }
+
+ @Test
+ public void run_replacesNullActiveRootAliasWithDefaultValue() throws Exception {
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ /*alias=*/ null);
+
+ when(mTestOnlyInsecureCertificateHelper.getDefaultCertificateAliasIfEmpty(null))
+ .thenReturn(TEST_ROOT_CERT_ALIAS); // override default.
+ mKeySyncTask.run();
+
+ verify(mTestOnlyInsecureCertificateHelper).getDefaultCertificateAliasIfEmpty(null);
+ }
+
+ @Test
public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
@@ -391,7 +500,8 @@
CREDENTIAL_TYPE_PASSWORD,
"password",
/*credentialUpdated=*/ false,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
@@ -417,7 +527,8 @@
CREDENTIAL_TYPE_PASSWORD,
/*credential=*/ "1234",
/*credentialUpdated=*/ false,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
@@ -444,7 +555,8 @@
CREDENTIAL_TYPE_PATTERN,
"12345",
/*credentialUpdated=*/ false,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
@@ -525,7 +637,8 @@
/*credentialType=*/ 3,
"12345",
/*credentialUpdated=*/ false,
- mPlatformKeyManager);
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
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 f5f5027..f4ec867 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
@@ -27,6 +27,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -67,6 +68,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.io.File;
import java.nio.charset.StandardCharsets;
@@ -93,6 +95,8 @@
private static final String ROOT_CERTIFICATE_ALIAS = "";
private static final String DEFAULT_ROOT_CERT_ALIAS =
TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
+ private static final String INSECURE_CERTIFICATE_ALIAS =
+ TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS;
private static final String TEST_SESSION_ID = "karlin";
private static final byte[] TEST_PUBLIC_KEY = new byte[] {
(byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -160,6 +164,7 @@
@Mock private KeyguardManager mKeyguardManager;
@Mock private PlatformKeyManager mPlatformKeyManager;
@Mock private ApplicationKeyStorage mApplicationKeyStorage;
+ @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
@@ -189,14 +194,14 @@
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
- KeyStore.getInstance(),
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
mRecoverySnapshotStorage,
mMockListenersStorage,
mPlatformKeyManager,
- mApplicationKeyStorage);
+ mApplicationKeyStorage,
+ mTestOnlyInsecureCertificateHelper);
}
@After
@@ -206,24 +211,6 @@
}
@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();
@@ -260,7 +247,7 @@
@Test
public void removeKey_removesAKey() throws Exception {
int uid = Binder.getCallingUid();
- mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
+ mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
@@ -271,7 +258,7 @@
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);
@@ -301,6 +288,9 @@
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
TestData.getCertXmlWithSerial(certSerial));
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS);
+
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
@@ -310,6 +300,67 @@
}
@Test
+ public void initRecoveryService_triesToFilterRootAlias() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getDefaultCertificateAliasIfEmpty(eq(ROOT_CERTIFICATE_ALIAS));
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+
+ String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
+ assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
+
+ }
+
+ @Test
+ public void initRecoveryService_usesProdCertificateForEmptyRootAlias() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ "",
+ TestData.getCertXmlWithSerial(certSerial));
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getDefaultCertificateAliasIfEmpty(eq(""));
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+
+ String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
+ assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
+ }
+
+ @Test
+ public void initRecoveryService_usesProdCertificateForNullRootAlias() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ null,
+ TestData.getCertXmlWithSerial(certSerial));
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getDefaultCertificateAliasIfEmpty(null);
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+
+ String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
+ assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
+ }
+
+ @Test
public void initRecoveryService_regeneratesCounterId() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
@@ -418,6 +469,24 @@
}
@Test
+ public void initRecoveryServiceWithSigFile_usesProdCertificateForNullRootAlias()
+ throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ /*rootCertificateAlias=*/null, TestData.getCertXml(), TestData.getSigXml());
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getDefaultCertificateAliasIfEmpty(null);
+
+ verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+ .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+ }
+
+ @Test
public void initRecoveryServiceWithSigFile_throwsIfNullCertFile() throws Exception {
try {
mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
@@ -454,6 +523,18 @@
}
@Test
+ public void initRecoveryServiceWithSigFile_throwsIfTestAliasUsedWithProdCert()
+ throws Exception {
+ try {
+ mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+ INSECURE_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("signature over the cert file is invalid");
+ }
+ }
+
+ @Test
public void initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature() throws Exception {
byte[] modifiedCertXml = TestData.getCertXml();
modifiedCertXml[modifiedCertXml.length - 1] = 0; // Change the last new line char to a zero
@@ -713,7 +794,8 @@
}
@Test
- public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
+ public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys()
+ throws Exception {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
TEST_PUBLIC_KEY,
@@ -793,7 +875,8 @@
}
@Test
- public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
+ public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails()
+ throws Exception {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
TEST_PUBLIC_KEY,
@@ -955,7 +1038,7 @@
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/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index 4b059c6..9b2c853 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -14,8 +14,12 @@
import java.security.cert.CertPath;
import java.security.spec.ECPrivateKeySpec;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
public final class TestData {
+ private static final String KEY_ALGORITHM = "AES";
private static final long DEFAULT_SERIAL = 1000;
private static final String CERT_PATH_ENCODING = "PkiPath";
@@ -308,4 +312,10 @@
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
}
+
+ public static SecretKey generateKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ keyGenerator.init(/*keySize=*/ 256);
+ return keyGenerator.generateKey();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
new file mode 100644
index 0000000..bc50c9e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -0,0 +1,128 @@
+package com.android.server.locksettings.recoverablekeystore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keystore.recovery.TrustedRootCertificates;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import javax.crypto.SecretKey;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TestOnlyInsecureCertificateHelperTest {
+ private final TestOnlyInsecureCertificateHelper mHelper
+ = new TestOnlyInsecureCertificateHelper();
+
+ @Test
+ public void testDoesCredentailSupportInsecureMode_forNonWhitelistedPassword() throws Exception {
+ assertThat(mHelper.doesCredentailSupportInsecureMode(
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "secret12345")).isFalse();
+ assertThat(mHelper.doesCredentailSupportInsecureMode(
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "1234")).isFalse();
+ }
+
+ @Test
+ public void testDoesCredentailSupportInsecureMode_forWhitelistedPassword() throws Exception {
+ assertThat(mHelper.doesCredentailSupportInsecureMode(
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isTrue();
+
+ assertThat(mHelper.doesCredentailSupportInsecureMode(
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "12")).isTrue();
+ }
+
+ @Test
+ public void testDoesCredentailSupportInsecureMode_Pattern() throws Exception {
+ assertThat(mHelper.doesCredentailSupportInsecureMode(
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse();
+ assertThat(mHelper.doesCredentailSupportInsecureMode(
+ LockPatternUtils.CREDENTIAL_TYPE_NONE,
+ TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse();
+ }
+
+ @Test
+ public void testIsTestOnlyCertificate() throws Exception {
+ assertThat(mHelper.isTestOnlyCertificate(
+ TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS)).isFalse();
+ assertThat(mHelper.isTestOnlyCertificate(
+ TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isTrue();
+ assertThat(mHelper.isTestOnlyCertificate(
+ "UNKNOWN_ALIAS")).isFalse();
+ }
+
+ @Test
+ public void testKeepOnlyWhitelistedInsecureKeys_emptyKeysList() throws Exception {
+ Map<String, SecretKey> rawKeys = new HashMap<>();
+ Map<String, SecretKey> expectedResult = new HashMap<>();
+
+ Map<String, SecretKey> filteredKeys =
+ mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+ assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+ assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
+ }
+
+ @Test
+ public void testKeepOnlyWhitelistedInsecureKeys_singleNonWhitelistedKey() throws Exception {
+ Map<String, SecretKey> rawKeys = new HashMap<>();
+ Map<String, SecretKey> expectedResult = new HashMap<>();
+
+ String alias = "secureAlias";
+ rawKeys.put(alias, TestData.generateKey());
+
+ Map<String, SecretKey> filteredKeys =
+ mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+ assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+ assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+ }
+
+ @Test
+ public void testKeepOnlyWhitelistedInsecureKeys_singleWhitelistedKey() throws Exception {
+ Map<String, SecretKey> rawKeys = new HashMap<>();
+ Map<String, SecretKey> expectedResult = new HashMap<>();
+
+ String alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
+ rawKeys.put(alias, TestData.generateKey());
+ expectedResult.put(alias, rawKeys.get(alias));
+
+ Map<String, SecretKey> filteredKeys =
+ mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+ assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+ assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+ }
+
+ @Test
+ public void testKeepOnlyWhitelistedInsecureKeys() throws Exception {
+ Map<String, SecretKey> rawKeys = new HashMap<>();
+ Map<String, SecretKey> expectedResult = new HashMap<>();
+
+ String alias = "SECURE_ALIAS" + TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
+ rawKeys.put(alias, TestData.generateKey());
+
+ alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "1";
+ rawKeys.put(alias, TestData.generateKey());
+ expectedResult.put(alias, rawKeys.get(alias));
+
+ alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "2";
+ rawKeys.put(alias, TestData.generateKey());
+ expectedResult.put(alias, rawKeys.get(alias));
+
+ Map<String, SecretKey> filteredKeys =
+ mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+ assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+ assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
index 6c2958e..2f4da86 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
@@ -45,6 +45,7 @@
private static final int MAX_ATTEMPTS = 21;
private static final byte[] SERVER_PARAMS = new byte[] { 8, 2, 4 };
private static final byte[] KEY_BLOB = new byte[] { 124, 53, 53, 53 };
+ private static final byte[] PUBLIC_KEY_BLOB = new byte[] { 6, 6, 6, 6, 6, 6, 7 };
private static final CertPath CERT_PATH = TestData.CERT_PATH_1;
private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PASSWORD;
@@ -93,6 +94,11 @@
}
@Test
+ public void roundTrip_persistsBackendPublicKey() throws Exception {
+ assertThat(roundTrip().getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY_BLOB);
+ }
+
+ @Test
public void roundTrip_persistsParamsList() throws Exception {
assertThat(roundTrip().getKeyChainProtectionParams()).hasSize(1);
}
@@ -163,6 +169,12 @@
assertThat(roundTripKeys().get(2).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_3_BYTES);
}
+ @Test
+ public void serialize_doesNotThrowForNullPublicKey() throws Exception {
+ KeyChainSnapshotSerializer.serialize(
+ createTestKeyChainSnapshotNoPublicKey(), new ByteArrayOutputStream());
+ }
+
private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
return roundTrip().getWrappedApplicationKeys();
}
@@ -180,6 +192,41 @@
}
private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
+ return new KeyChainSnapshot.Builder()
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(SNAPSHOT_VERSION)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+ .setWrappedApplicationKeys(createKeys())
+ .setTrustedHardwareCertPath(CERT_PATH)
+ .setTrustedHardwarePublicKey(PUBLIC_KEY_BLOB)
+ .build();
+ }
+
+ private static KeyChainSnapshot createTestKeyChainSnapshotNoPublicKey() throws Exception {
+ return new KeyChainSnapshot.Builder()
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(SNAPSHOT_VERSION)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+ .setWrappedApplicationKeys(createKeys())
+ .setTrustedHardwareCertPath(CERT_PATH)
+ .build();
+ }
+
+ private static List<WrappedApplicationKey> createKeys() {
+ ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+ keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+ keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+ keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+ return keyList;
+ }
+
+ private static List<KeyChainProtectionParams> createKeyChainProtectionParamsList() {
KeyDerivationParams keyDerivationParams =
KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
@@ -191,22 +238,7 @@
ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
new ArrayList<>(1);
keyChainProtectionParamsList.add(keyChainProtectionParams);
-
- ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
- keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
- keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
- keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
-
- return new KeyChainSnapshot.Builder()
- .setCounterId(COUNTER_ID)
- .setSnapshotVersion(SNAPSHOT_VERSION)
- .setServerParams(SERVER_PARAMS)
- .setMaxAttempts(MAX_ATTEMPTS)
- .setEncryptedRecoveryKeyBlob(KEY_BLOB)
- .setKeyChainProtectionParams(keyChainProtectionParamsList)
- .setWrappedApplicationKeys(keyList)
- .setTrustedHardwareCertPath(CERT_PATH)
- .build();
+ return keyChainProtectionParamsList;
}
private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index c772956..ad14c3a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -1,27 +1,82 @@
package com.android.server.locksettings.recoverablekeystore.storage;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import android.content.Context;
+import android.os.FileUtils;
+import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.server.locksettings.recoverablekeystore.TestData;
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecoverySnapshotStorageTest {
- private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
- createMinimalKeyChainSnapshot();
+ private static final int COUNTER_ID = 432546;
+ private static final int MAX_ATTEMPTS = 10;
+ private static final byte[] SERVER_PARAMS = new byte[] { 12, 8, 2, 4, 15, 64 };
+ private static final byte[] KEY_BLOB = new byte[] { 124, 56, 53, 99, 0, 0, 1 };
+ private static final CertPath CERT_PATH = TestData.CERT_PATH_2;
+ private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+ private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PATTERN;
+ private static final byte[] SALT = new byte[] { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
+ private static final int MEMORY_DIFFICULTY = 12;
+ private static final byte[] SECRET = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 };
- private final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+ private static final String TEST_KEY_1_ALIAS = "alias1";
+ private static final byte[] TEST_KEY_1_BYTES = new byte[] { 100, 32, 43, 66, 77, 88 };
+
+ private static final String TEST_KEY_2_ALIAS = "alias11";
+ private static final byte[] TEST_KEY_2_BYTES = new byte[] { 100, 0, 0, 99, 33, 11 };
+
+ private static final String TEST_KEY_3_ALIAS = "alias111";
+ private static final byte[] TEST_KEY_3_BYTES = new byte[] { 1, 1, 1, 0, 2, 8, 100 };
+
+ private static final int TEST_UID = 1000;
+ private static final String SNAPSHOT_DIRECTORY = "recoverablekeystore/snapshots";
+ private static final String SNAPSHOT_FILE_PATH = "1000.xml";
+ private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
+ private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
+ createTestKeyChainSnapshot(1);
+
+ private Context mContext;
+ private RecoverySnapshotStorage mRecoverySnapshotStorage;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mRecoverySnapshotStorage = new RecoverySnapshotStorage(mContext.getFilesDir());
+ }
+
+ @After
+ public void tearDown() {
+ File file = new File(mContext.getFilesDir(), SNAPSHOT_TOP_LEVEL_DIRECTORY);
+ FileUtils.deleteContentsAndDir(file);
+ }
@Test
public void get_isNullForNonExistentSnapshot() {
@@ -30,37 +85,153 @@
@Test
public void get_returnsSetSnapshot() {
- int userId = 1000;
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
- mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
-
- assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(userId));
+ assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(TEST_UID));
}
@Test
- public void remove_removesSnapshots() {
- int userId = 1000;
+ public void get_readsFromDiskIfNoneInMemory() {
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+ RecoverySnapshotStorage storage = new RecoverySnapshotStorage(mContext.getFilesDir());
- mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
- mRecoverySnapshotStorage.remove(userId);
-
- assertNull(mRecoverySnapshotStorage.get(1000));
+ assertKeyChainSnapshotsAreEqual(MINIMAL_KEYCHAIN_SNAPSHOT, storage.get(TEST_UID));
}
- private static KeyChainSnapshot createMinimalKeyChainSnapshot() {
+ @Test
+ public void get_deletesFileIfItIsInvalidSnapshot() throws Exception {
+ File folder = new File(mContext.getFilesDir(), SNAPSHOT_DIRECTORY);
+ folder.mkdirs();
+ File file = new File(folder, SNAPSHOT_FILE_PATH);
+ byte[] fileContents = "<keyChainSnapshot></keyChainSnapshot>".getBytes(
+ StandardCharsets.UTF_8);
+ Files.write(fileContents, file);
+ assertTrue(file.exists());
+
+ assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+
+ assertFalse(file.exists());
+ }
+
+ @Test
+ public void put_overwritesOldFiles() {
+ int snapshotVersion = 2;
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ mRecoverySnapshotStorage.put(TEST_UID, createTestKeyChainSnapshot(snapshotVersion));
+
+ KeyChainSnapshot snapshot = new RecoverySnapshotStorage(mContext.getFilesDir())
+ .get(TEST_UID);
+ assertEquals(snapshotVersion, snapshot.getSnapshotVersion());
+ }
+
+ @Test
+ public void put_doesNotThrowIfCannotCreateFiles() throws Exception {
+ File evilFile = new File(mContext.getFilesDir(), "recoverablekeystore");
+ Files.write(new byte[] { 1 }, evilFile);
+
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+ }
+
+ @Test
+ public void remove_removesSnapshotsFromMemory() {
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+ mRecoverySnapshotStorage.remove(TEST_UID);
+
+ assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+ }
+
+ @Test
+ public void remove_removesSnapshotsFromDisk() {
+ mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+ new RecoverySnapshotStorage(mContext.getFilesDir()).remove(TEST_UID);
+
+ assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+ }
+
+ private void assertKeyChainSnapshotsAreEqual(KeyChainSnapshot a, KeyChainSnapshot b) {
+ assertEquals(b.getCounterId(), a.getCounterId());
+ assertEquals(b.getSnapshotVersion(), a.getSnapshotVersion());
+ assertArrayEquals(b.getServerParams(), a.getServerParams());
+ assertEquals(b.getMaxAttempts(), a.getMaxAttempts());
+ assertArrayEquals(b.getEncryptedRecoveryKeyBlob(), a.getEncryptedRecoveryKeyBlob());
+ assertEquals(b.getTrustedHardwareCertPath(), a.getTrustedHardwareCertPath());
+
+ List<WrappedApplicationKey> aKeys = a.getWrappedApplicationKeys();
+ List<WrappedApplicationKey> bKeys = b.getWrappedApplicationKeys();
+ assertEquals(bKeys.size(), aKeys.size());
+ for (int i = 0; i < aKeys.size(); i++) {
+ assertWrappedApplicationKeysAreEqual(aKeys.get(i), bKeys.get(i));
+ }
+
+ List<KeyChainProtectionParams> aParams = a.getKeyChainProtectionParams();
+ List<KeyChainProtectionParams> bParams = b.getKeyChainProtectionParams();
+ assertEquals(bParams.size(), aParams.size());
+ for (int i = 0; i < aParams.size(); i++) {
+ assertKeyChainProtectionParamsAreEqual(aParams.get(i), bParams.get(i));
+ }
+ }
+
+ private void assertWrappedApplicationKeysAreEqual(
+ WrappedApplicationKey a, WrappedApplicationKey b) {
+ assertEquals(b.getAlias(), a.getAlias());
+ assertArrayEquals(b.getEncryptedKeyMaterial(), a.getEncryptedKeyMaterial());
+ }
+
+ private void assertKeyChainProtectionParamsAreEqual(
+ KeyChainProtectionParams a, KeyChainProtectionParams b) {
+ assertEquals(b.getUserSecretType(), a.getUserSecretType());
+ assertEquals(b.getLockScreenUiFormat(), a.getLockScreenUiFormat());
+ assertKeyDerivationParamsAreEqual(a.getKeyDerivationParams(), b.getKeyDerivationParams());
+ }
+
+ private void assertKeyDerivationParamsAreEqual(KeyDerivationParams a, KeyDerivationParams b) {
+ assertEquals(b.getAlgorithm(), a.getAlgorithm());
+ assertEquals(b.getMemoryDifficulty(), a.getMemoryDifficulty());
+ assertArrayEquals(b.getSalt(), a.getSalt());
+ }
+
+ private static KeyChainSnapshot createTestKeyChainSnapshot(int snapshotVersion) {
+ KeyDerivationParams keyDerivationParams =
+ KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
+ KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
+ .setKeyDerivationParams(keyDerivationParams)
+ .setUserSecretType(SECRET_TYPE)
+ .setLockScreenUiFormat(LOCK_SCREEN_UI)
+ .setSecret(SECRET)
+ .build();
+ ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
+ new ArrayList<>(1);
+ keyChainProtectionParamsList.add(keyChainProtectionParams);
+
+ ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+ keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+ keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+ keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+
try {
return new KeyChainSnapshot.Builder()
- .setCounterId(1)
- .setSnapshotVersion(1)
- .setServerParams(new byte[0])
- .setMaxAttempts(10)
- .setEncryptedRecoveryKeyBlob(new byte[0])
- .setKeyChainProtectionParams(new ArrayList<>())
- .setWrappedApplicationKeys(new ArrayList<>())
- .setTrustedHardwareCertPath(TestData.CERT_PATH_1)
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(snapshotVersion)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(keyChainProtectionParamsList)
+ .setWrappedApplicationKeys(keyList)
+ .setTrustedHardwareCertPath(CERT_PATH)
.build();
} catch (CertificateException e) {
throw new RuntimeException(e);
}
}
+
+ private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
+ return new WrappedApplicationKey.Builder()
+ .setAlias(alias)
+ .setEncryptedKeyMaterial(bytes)
+ .build();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 553d658..95361f0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,14 +17,20 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IInterface;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +56,7 @@
* atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
*/
@SmallTest
-@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class RemoteAnimationControllerTest extends WindowTestsBase {
@@ -67,6 +73,7 @@
public void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
+ when(mMockRunner.asBinder()).thenReturn(new Binder());
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
mAdapter.setCallingPid(123);
sWm.mH.runWithScissors(() -> {
@@ -166,7 +173,7 @@
@Test
public void testZeroAnimations() throws Exception {
mController.goodToGo();
- verifyZeroInteractions(mMockRunner);
+ verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@Test
@@ -175,7 +182,7 @@
mController.createAnimationAdapter(win.mAppToken,
new Point(50, 100), new Rect(50, 100, 150, 150));
mController.goodToGo();
- verifyZeroInteractions(mMockRunner);
+ verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@Test
@@ -206,7 +213,12 @@
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
win.mAppToken.removeImmediately();
mController.goodToGo();
- verifyZeroInteractions(mMockRunner);
+ verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
}
+
+ private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
+ verify(binder, atLeast(0)).asBinder();
+ verifyNoMoreInteractions(binder);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 6d9167f..1af344b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -445,8 +445,7 @@
}
@Override
- public int rotationForOrientationLw(int orientation,
- int lastRotation) {
+ public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
return rotationToReport;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 09d88fd..0815876 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -367,8 +367,8 @@
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
mHelper.setShowBadge(PKG, UID, true);
@@ -427,10 +427,10 @@
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, false);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
+ mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
mHelper.setShowBadge(PKG, UID, true);
@@ -481,7 +481,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
@@ -507,7 +507,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
loadStreamXml(baos, true);
@@ -528,7 +528,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
loadStreamXml(baos, true);
@@ -569,7 +569,7 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(null, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
loadStreamXml(baos, true);
@@ -593,9 +593,9 @@
mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
@@ -701,7 +701,7 @@
@Test
public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
@@ -721,7 +721,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
loadStreamXml(baos, false);
@@ -734,36 +734,39 @@
mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
}
@Test
public void testCreateChannel_badImportance() throws Exception {
try {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
+ true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
try {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
+ true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
try {
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
+ true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true);
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
}
@@ -777,7 +780,7 @@
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.createNotificationChannel(PKG, UID, channel, false);
+ mHelper.createNotificationChannel(PKG, UID, channel, false, false);
// same id, try to update all fields
final NotificationChannel channel2 =
@@ -824,7 +827,7 @@
public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, false);
+ mHelper.createNotificationChannel(PKG, UID, channel, false, false);
assertTrue(mHelper.canShowBadge(PKG, UID));
assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
@@ -865,7 +868,7 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
NotificationChannel savedChannel =
mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -894,7 +897,7 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
NotificationChannel savedChannel =
mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -920,7 +923,7 @@
@Test
public void testLockFields_soundAndVibration() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setSound(new Uri.Builder().scheme("test").build(),
@@ -944,7 +947,7 @@
@Test
public void testLockFields_vibrationAndLights() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setVibrationPattern(new long[]{7945, 46 ,246});
@@ -964,7 +967,7 @@
@Test
public void testLockFields_lightsAndImportance() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setLightColor(Color.GREEN);
@@ -984,7 +987,7 @@
@Test
public void testLockFields_visibilityAndDndAndBadge() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
assertEquals(0,
mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
.getUserLockedFields());
@@ -1029,7 +1032,7 @@
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{100, 67, 145, 156});
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
// Does not return deleted channel
@@ -1058,8 +1061,8 @@
NotificationChannel channel2 =
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
channelMap.put(channel2.getId(), channel2);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
@@ -1091,9 +1094,9 @@
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true);
- mHelper.createNotificationChannel(PKG, UID, channel3, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
@@ -1109,14 +1112,14 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+ mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
// No long deleted, using old settings
compareChannels(channel,
@@ -1128,7 +1131,7 @@
assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
}
@@ -1136,7 +1139,7 @@
public void testCreateChannel_defaultChannelId() throws Exception {
try {
mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true);
+ NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
fail("Allowed to create default channel");
} catch (IllegalArgumentException e) {
// pass
@@ -1150,13 +1153,13 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+ mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
// Old settings not overridden
compareChannels(channel,
@@ -1169,7 +1172,7 @@
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setSound(sound, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
assertEquals(sound, mHelper.getNotificationChannel(
PKG, UID, channel.getId(), false).getSound());
}
@@ -1181,8 +1184,8 @@
NotificationChannel channel2 =
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
- mHelper.createNotificationChannel(PKG, UID, channel2, false);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
@@ -1205,9 +1208,9 @@
mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
- mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true);
- mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true);
- mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true);
+ mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
+ mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
@@ -1264,14 +1267,14 @@
// Deleted
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
// Not deleted
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
@@ -1302,7 +1305,7 @@
@Test
public void testOnPackageChange_downgradeTargetSdk() throws Exception {
// create channel as api 26
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+ mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
// install new app version targeting 25
final ApplicationInfo legacy = new ApplicationInfo();
@@ -1338,7 +1341,7 @@
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup("garbage");
try {
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
fail("Created a channel with a bad group");
} catch (IllegalArgumentException e) {
}
@@ -1351,7 +1354,7 @@
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
assertEquals(ncg.getId(),
mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
@@ -1369,20 +1372,20 @@
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
NotificationChannel channel1a =
new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1a.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1a, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
NotificationChannel channel2 =
new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
channel2.setGroup(ncg2.getId());
- mHelper.createNotificationChannel(PKG, UID, channel2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
NotificationChannel channel3 =
new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel3, true);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
List<NotificationChannelGroup> actual =
mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
@@ -1416,7 +1419,7 @@
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
channel1.setImportance(IMPORTANCE_LOW);
@@ -1436,12 +1439,12 @@
@Test
public void testCreateChannel_updateName() throws Exception {
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertEquals("hello", actual.getName());
nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertEquals("goodbye", actual.getName());
@@ -1455,13 +1458,13 @@
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
mHelper.createNotificationChannelGroup(PKG, UID, group, true);
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertNull(actual.getGroup());
nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
nc.setGroup(group.getId());
- mHelper.createNotificationChannel(PKG, UID, nc, true);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
assertNotNull(actual.getGroup());
@@ -1486,7 +1489,7 @@
int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
for (int j = 0; j < numChannels; j++) {
mHelper.createNotificationChannel(pkgName, UID,
- new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true);
+ new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
}
expectedChannels.put(pkgName, numChannels);
}
@@ -1621,10 +1624,10 @@
c.setGroup(group.getId());
NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, a, true);
- mHelper.createNotificationChannel(PKG, UID, b, true);
- mHelper.createNotificationChannel(PKG, UID, c, true);
- mHelper.createNotificationChannel(PKG, UID, d, true);
+ mHelper.createNotificationChannel(PKG, UID, a, true, false);
+ mHelper.createNotificationChannel(PKG, UID, b, true, false);
+ mHelper.createNotificationChannel(PKG, UID, c, true, false);
+ mHelper.createNotificationChannel(PKG, UID, d, true, false);
mHelper.deleteNotificationChannel(PKG, UID, c.getId());
NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
@@ -1641,22 +1644,31 @@
@Test
public void testAndroidPkgCanBypassDnd_creation() {
-
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
.canBypassDnd());
}
@Test
+ public void testDndPkgCanBypassDnd_creation() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ test.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ }
+
+ @Test
public void testNormalPkgCannotBypassDnd_creation() {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, test, true);
+ mHelper.createNotificationChannel(PKG, 1000, test, true, false);
assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
}
@@ -1664,11 +1676,11 @@
@Test
public void testAndroidPkgCanBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
.canBypassDnd());
@@ -1678,12 +1690,24 @@
}
@Test
- public void testNormalPkgCannotBypassDnd_update() {
+ public void testDndPkgCanBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, 1000, test, true);
+ mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, update, true);
+ mHelper.createNotificationChannel(PKG, UID, update, true, true);
+
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ }
+
+ @Test
+ public void testNormalPkgCannotBypassDnd_update() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ update.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG, 1000, update, true, false);
assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
}
}
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 36136a8..ce74457 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 @@
}
@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/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 1073a80..5e2a364 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -15,8 +15,10 @@
package com.android.server.slice;
import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -35,9 +37,11 @@
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -63,6 +67,7 @@
private SliceManagerService mService;
private PinnedSliceState mCreatedSliceState;
private IBinder mToken = new Binder();
+ private TestableContext mContextSpy;
@Before
public void setup() {
@@ -72,7 +77,8 @@
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
- mService = spy(new SliceManagerService(mContext, TestableLooper.get(this).getLooper()));
+ mContextSpy = spy(mContext);
+ mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper()));
mCreatedSliceState = mock(PinnedSliceState.class);
doReturn(mCreatedSliceState).when(mService).createPinnedSlice(eq(TEST_URI), anyString());
}
@@ -103,4 +109,23 @@
verify(mCreatedSliceState, never()).destroy();
}
+ @Test
+ public void testCheckAutoGrantPermissions() throws RemoteException {
+ String[] testPerms = new String[] {
+ "perm1",
+ "perm2",
+ };
+ when(mContextSpy.checkUriPermission(any(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(PERMISSION_DENIED);
+ when(mContextSpy.checkPermission("perm1", Process.myPid(), Process.myUid()))
+ .thenReturn(PERMISSION_DENIED);
+ when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
+ .thenReturn(PERMISSION_GRANTED);
+ mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+ Process.myUid(), testPerms);
+
+ verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
+ verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
+ }
+
}
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 571ed00a..5f01518 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -24,6 +24,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -186,6 +187,7 @@
static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
/** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
+ static final int MSG_REPORT_EXEMPTED_SYNC_START = 12;
long mCheckIdleIntervalMillis;
long mAppIdleParoleIntervalMillis;
@@ -202,6 +204,8 @@
long mPredictionTimeoutMillis;
/** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
long mSyncAdapterTimeoutMillis;
+ /** Maximum time an exempted sync should keep the buckets elevated. */
+ long mExemptedSyncAdapterTimeoutMillis;
/** Maximum time a system interaction should keep the buckets elevated. */
long mSystemInteractionTimeoutMillis;
@@ -375,6 +379,21 @@
}
}
+ void reportExemptedSyncStart(String packageName, int userId) {
+ if (!mAppIdleEnabled) return;
+
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+
+ synchronized (mAppIdleLock) {
+ AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START,
+ 0,
+ elapsedRealtime + mExemptedSyncAdapterTimeoutMillis);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ appUsage.currentBucket, appUsage.bucketingReason, false);
+ }
+ }
+
void setChargingState(boolean charging) {
synchronized (mAppIdleLock) {
if (mCharging != charging) {
@@ -1274,6 +1293,11 @@
.sendToTarget();
}
+ void postReportExemptedSyncStart(String packageName, int userId) {
+ mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
+ .sendToTarget();
+ }
+
void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
synchronized (mAppIdleLock) {
mAppIdleHistory.dump(idpw, userId, pkg);
@@ -1488,6 +1512,11 @@
checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
mInjector.elapsedRealtime());
break;
+
+ case MSG_REPORT_EXEMPTED_SYNC_START:
+ reportExemptedSyncStart((String) msg.obj, msg.arg1);
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -1550,6 +1579,7 @@
"system_update_usage_duration";
private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
+ private static final String KEY_EXEMPTED_SYNC_HOLD_DURATION = "exempted_sync_duration";
private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
"system_interaction_duration";
public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
@@ -1557,6 +1587,7 @@
public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
+ public static final long DEFAULT_EXEMPTED_SYNC_TIMEOUT = 10 * ONE_MINUTE;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1632,6 +1663,9 @@
mSyncAdapterTimeoutMillis = mParser.getDurationMillis
(KEY_SYNC_ADAPTER_HOLD_DURATION,
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT);
+ mExemptedSyncAdapterTimeoutMillis = mParser.getDurationMillis
+ (KEY_EXEMPTED_SYNC_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : DEFAULT_EXEMPTED_SYNC_TIMEOUT);
mSystemInteractionTimeoutMillis = mParser.getDurationMillis
(KEY_SYSTEM_INTERACTION_HOLD_DURATION,
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2258b24..1fbc27b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1279,5 +1279,10 @@
public void onAdminDataAvailable() {
mAppStandby.onAdminDataAvailable();
}
+
+ @Override
+ public void reportExemptedSyncStart(String packageName, int userId) {
+ mAppStandby.postReportExemptedSyncStart(packageName, userId);
+ }
}
}
diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
new file mode 100644
index 0000000..25e1474
--- /dev/null
+++ b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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 android.app.usage;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats.Entry;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkStatsManagerTest {
+
+ private @Mock INetworkStatsService mService;
+ private @Mock INetworkStatsSession mStatsSession;
+
+ private NetworkStatsManager mManager;
+
+ // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp.
+ private static final int MATCH_MOBILE_ALL = 1;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService);
+ }
+
+ @Test
+ public void testQueryDetails() throws RemoteException {
+ final String subscriberId = "subid";
+ final long startTime = 1;
+ final long endTime = 100;
+ final int uid1 = 10001;
+ final int uid2 = 10002;
+ final int uid3 = 10003;
+
+ Entry uid1Entry1 = new Entry("if1", uid1,
+ android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+ 100, 10, 200, 20, 0);
+
+ Entry uid1Entry2 = new Entry(
+ "if2", uid1,
+ android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+ 100, 10, 200, 20, 0);
+
+ Entry uid2Entry1 = new Entry("if1", uid2,
+ android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+ 150, 10, 250, 20, 0);
+
+ Entry uid2Entry2 = new Entry(
+ "if2", uid2,
+ android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+ 150, 10, 250, 20, 0);
+
+ NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2);
+ history1.recordData(10, 20, uid1Entry1);
+ history1.recordData(20, 30, uid1Entry2);
+
+ NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2);
+ history1.recordData(30, 40, uid2Entry1);
+ history1.recordData(35, 45, uid2Entry2);
+
+
+ when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
+ when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 });
+
+ when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
+ eq(uid1), eq(android.net.NetworkStats.SET_ALL),
+ eq(android.net.NetworkStats.TAG_NONE),
+ eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
+ .then((InvocationOnMock inv) -> {
+ NetworkTemplate template = inv.getArgument(0);
+ assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
+ assertEquals(subscriberId, template.getSubscriberId());
+ return history1;
+ });
+
+ when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
+ eq(uid2), eq(android.net.NetworkStats.SET_ALL),
+ eq(android.net.NetworkStats.TAG_NONE),
+ eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
+ .then((InvocationOnMock inv) -> {
+ NetworkTemplate template = inv.getArgument(0);
+ assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
+ assertEquals(subscriberId, template.getSubscriberId());
+ return history2;
+ });
+
+
+ NetworkStats stats = mManager.queryDetails(
+ ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime);
+
+ NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+
+ // First 2 buckets exactly match entry timings
+ assertTrue(stats.getNextBucket(bucket));
+ assertEquals(10, bucket.getStartTimeStamp());
+ assertEquals(20, bucket.getEndTimeStamp());
+ assertBucketMatches(uid1Entry1, bucket);
+
+ assertTrue(stats.getNextBucket(bucket));
+ assertEquals(20, bucket.getStartTimeStamp());
+ assertEquals(30, bucket.getEndTimeStamp());
+ assertBucketMatches(uid1Entry2, bucket);
+
+ // 30 -> 40: contains uid2Entry1 and half of uid2Entry2
+ assertTrue(stats.getNextBucket(bucket));
+ assertEquals(30, bucket.getStartTimeStamp());
+ assertEquals(40, bucket.getEndTimeStamp());
+ assertEquals(225, bucket.getRxBytes());
+ assertEquals(15, bucket.getRxPackets());
+ assertEquals(375, bucket.getTxBytes());
+ assertEquals(30, bucket.getTxPackets());
+
+ // 40 -> 50: contains half of uid2Entry2
+ assertTrue(stats.getNextBucket(bucket));
+ assertEquals(40, bucket.getStartTimeStamp());
+ assertEquals(50, bucket.getEndTimeStamp());
+ assertEquals(75, bucket.getRxBytes());
+ assertEquals(5, bucket.getRxPackets());
+ assertEquals(125, bucket.getTxBytes());
+ assertEquals(10, bucket.getTxPackets());
+
+ assertFalse(stats.hasNextBucket());
+ }
+
+ @Test
+ public void testQueryDetails_NoSubscriberId() throws RemoteException {
+ final long startTime = 1;
+ final long endTime = 100;
+ final int uid1 = 10001;
+ final int uid2 = 10002;
+
+ when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
+ when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 });
+
+ NetworkStats stats = mManager.queryDetails(
+ ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);
+
+ when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
+ anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()))
+ .thenReturn(new NetworkStatsHistory(10, 0));
+
+ verify(mStatsSession, times(1)).getHistoryIntervalForUid(
+ argThat((NetworkTemplate t) ->
+ // No subscriberId: MATCH_MOBILE_WILDCARD template
+ t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
+ eq(uid1), eq(android.net.NetworkStats.SET_ALL),
+ eq(android.net.NetworkStats.TAG_NONE),
+ eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+
+ verify(mStatsSession, times(1)).getHistoryIntervalForUid(
+ argThat((NetworkTemplate t) ->
+ // No subscriberId: MATCH_MOBILE_WILDCARD template
+ t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
+ eq(uid2), eq(android.net.NetworkStats.SET_ALL),
+ eq(android.net.NetworkStats.TAG_NONE),
+ eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+
+ assertFalse(stats.hasNextBucket());
+ }
+
+ private void assertBucketMatches(Entry expected,
+ NetworkStats.Bucket actual) {
+ assertEquals(expected.uid, actual.getUid());
+ assertEquals(expected.rxBytes, actual.getRxBytes());
+ assertEquals(expected.rxPackets, actual.getRxPackets());
+ assertEquals(expected.txBytes, actual.getTxBytes());
+ assertEquals(expected.txPackets, actual.getTxPackets());
+ }
+}
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 6bdfdc6..85e8361 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -22,8 +22,12 @@
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 @@
};
@Test
- public void testDefaultTruncLen() throws Exception {
- IpSecAlgorithm explicit =
+ 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), 256);
- IpSecAlgorithm implicit =
- 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 f186ee5..771faaf 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -62,7 +62,8 @@
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 0ca20de..970596d 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -179,7 +179,7 @@
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 @@
IpSecManager.UdpEncapsulationSocket encapSocket =
mIpSecManager.openUdpEncapsulationSocket();
- assertNotNull(encapSocket.getSocket());
+ assertNotNull(encapSocket.getFileDescriptor());
assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
encapSocket.close();
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/tests/net/java/android/net/util/InterfaceSetTest.java
new file mode 100644
index 0000000..8012838
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceSetTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.net.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceSetTest {
+ @Test
+ public void testNullNamesIgnored() {
+ final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
+ assertEquals(2, set.ifnames.size());
+ assertTrue(set.ifnames.contains("if1"));
+ assertTrue(set.ifnames.contains("if2"));
+ }
+
+ @Test
+ public void testToString() {
+ final InterfaceSet set = new InterfaceSet("if1", "if2");
+ final String setString = set.toString();
+ assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
+ }
+
+ @Test
+ public void testToString_Empty() {
+ final InterfaceSet set = new InterfaceSet(null, null);
+ assertEquals("[]", set.toString());
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
+ assertEquals(new InterfaceSet(null, null), new InterfaceSet());
+ assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
+ assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
+ assertFalse(new InterfaceSet().equals(null));
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index e692652..27f90f2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,8 +27,11 @@
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -36,6 +39,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -45,18 +49,29 @@
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
+import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
-import android.net.NetworkRequest;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -74,10 +89,16 @@
import android.telephony.CarrierConfigManager;
import android.test.mock.MockContentResolver;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.tethering.IControlsTethering;
+import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadHardwareInterface;
+import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import org.junit.After;
import org.junit.Before;
@@ -86,13 +107,21 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TetheringTest {
+ private static final int IFINDEX_OFFSET = 100;
+
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+ private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
+ private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
+ private static final String TEST_USB_IFNAME = "test_rndis0";
+ private static final String TEST_WLAN_IFNAME = "test_wlan0";
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@@ -103,16 +132,21 @@
@Mock private MockableSystemProperties mSystemProperties;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
- @Mock private TetheringDependencies mTetheringDependencies;
@Mock private UsbManager mUsbManager;
@Mock private WifiManager mWifiManager;
@Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+ @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+ @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+ @Mock private INetd mNetd;
+
+ private final MockTetheringDependencies mTetheringDependencies =
+ new MockTetheringDependencies();
// Like so many Android system APIs, these cannot be mocked because it is marked final.
// We have to use the real versions.
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
- private final String mTestIfname = "test_wlan0";
private Vector<Intent> mIntents;
private BroadcastInterceptingContext mServiceContext;
@@ -146,23 +180,122 @@
}
}
+ public class MockTetheringDependencies extends TetheringDependencies {
+ private StateMachine upstreamNetworkMonitorMasterSM;
+ private ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+
+ @Override
+ public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+ return mOffloadHardwareInterface;
+ }
+
+ @Override
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
+ StateMachine target, SharedLog log, int what) {
+ upstreamNetworkMonitorMasterSM = target;
+ return mUpstreamNetworkMonitor;
+ }
+
+ @Override
+ public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+ ipv6CoordinatorNotifyList = notifyList;
+ return mIPv6TetheringCoordinator;
+ }
+
+ @Override
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+ return mRouterAdvertisementDaemon;
+ }
+
+ @Override
+ public INetd getNetdService() {
+ return mNetd;
+ }
+
+ @Override
+ public InterfaceParams getInterfaceParams(String ifName) {
+ final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
+ TEST_MOBILE_IFNAME };
+ final int index = ArrayUtils.indexOf(ifaces, ifName);
+ assertTrue("Non-mocked interface: " + ifName, index >= 0);
+ return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
+ }
+
+ private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
+ boolean with464xlat) {
+ final NetworkInfo info = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, 0, null, null);
+ info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_MOBILE_IFNAME);
+
+ if (withIPv4) {
+ prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
+ }
+
+ if (withIPv6) {
+ prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
+ prop.addLinkAddress(
+ new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
+ NetworkConstants.RFC7421_PREFIX_LENGTH));
+ prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
+ NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
+ }
+
+ if (with464xlat) {
+ final LinkProperties stackedLink = new LinkProperties();
+ stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
+ stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+
+ prop.addStackedLink(stackedLink);
+ }
+
+
+ final NetworkCapabilities capabilities = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
+ return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
+ }
+
+ private static NetworkState buildMobileIPv4UpstreamState() {
+ return buildMobileUpstreamState(true, false, false);
+ }
+
+ private static NetworkState buildMobileIPv6UpstreamState() {
+ return buildMobileUpstreamState(false, true, false);
+ }
+
+ private static NetworkState buildMobileDualStackUpstreamState() {
+ return buildMobileUpstreamState(true, true, false);
+ }
+
+ private static NetworkState buildMobile464xlatUpstreamState() {
+ return buildMobileUpstreamState(false, true, true);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[] { "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
+ .thenReturn(new String[]{ "test_wlan\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
when(mNMService.listInterfaces())
- .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
+ .thenReturn(new String[] {
+ TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
when(mNMService.getInterfaceConfig(anyString()))
.thenReturn(new InterfaceConfiguration());
+ when(mRouterAdvertisementDaemon.start())
+ .thenReturn(true);
mServiceContext = new MockContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
@@ -176,8 +309,6 @@
};
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
- when(mTetheringDependencies.getOffloadHardwareInterface(
- any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
mLooper.getLooper(), mSystemProperties,
mTetheringDependencies);
@@ -264,10 +395,10 @@
}
private void verifyInterfaceServingModeStarted() throws Exception {
- verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
+ verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(1))
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
- verify(mNMService, times(1)).tetherInterface(mTestIfname);
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
}
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
@@ -287,7 +418,7 @@
// per-interface state machine to start up, and telling us that
// hotspot mode is to be started.
if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(mTestIfname, true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
}
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
@@ -297,27 +428,31 @@
// broadcast indicating that the interface is "available".
if (emulateInterfaceStatusChanged) {
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
}
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
- @Test
- public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+ private void prepareUsbTethering(NetworkState upstreamState) {
when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+ .thenReturn(upstreamState);
// Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(TETHERING_USB, null, false);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
- // Pretend we receive a USB connected broadcast. Here we also pretend
- // that the RNDIS function is somehow enabled, so that we see if we
- // might trip ourselves up.
- sendUsbBroadcast(true, false, true);
- mLooper.dispatchAll();
+ mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+ }
+
+ @Test
+ public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+ NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ prepareUsbTethering(upstreamState);
+
// This should produce no activity of any kind.
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
@@ -328,6 +463,10 @@
// Now we should see the start of tethering mechanics (in this case:
// tetherMatchingInterfaces() which starts by fetching all interfaces).
verify(mNMService, times(1)).listInterfaces();
+
+ // UpstreamNetworkMonitor should receive selected upstream
+ verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+ verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
}
@Test
@@ -348,26 +487,21 @@
// per-interface state machine to start up, and telling us that
// hotspot mode is to be started.
if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(mTestIfname, true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
}
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
mLooper.dispatchAll();
verifyInterfaceServingModeStarted();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
- // UpstreamNetworkMonitor will be started, and will register two callbacks:
- // a "listen all" and a "track default".
- verify(mConnectivityManager, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
- any(NetworkCallback.class), any(Handler.class));
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
+ verify(mUpstreamNetworkMonitor, times(1)).start();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyNoMoreInteractions(mConnectivityManager);
@@ -375,14 +509,14 @@
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mTethering.interfaceRemoved(mTestIfname);
+ mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
mLooper.dispatchAll();
- verify(mNMService, times(1)).untetherInterface(mTestIfname);
+ verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
// TODO: Why is {g,s}etInterfaceConfig() called more than once?
- verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+ verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
verifyNoMoreInteractions(mNMService);
@@ -390,7 +524,115 @@
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
- mTethering.getLastTetherError(mTestIfname));
+ mTethering.getLastTetherError(TEST_WLAN_IFNAME));
+ }
+
+ /**
+ * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+ */
+ private void sendIPv6TetherUpdates(NetworkState upstreamState) {
+ // IPv6TetheringCoordinator must have been notified of downstream
+ verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
+ argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
+ eq(IControlsTethering.STATE_TETHERED));
+
+ for (TetherInterfaceStateMachine tism :
+ mTetheringDependencies.ipv6CoordinatorNotifyList) {
+ NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
+ tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+ upstreamState.linkProperties.isIPv6Provisioned()
+ ? ipv6OnlyState.linkProperties
+ : null);
+ }
+ mLooper.dispatchAll();
+ }
+
+ private void runUsbTethering(NetworkState upstreamState) {
+ prepareUsbTethering(upstreamState);
+ sendUsbBroadcast(true, true, true);
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_IPv4() throws Exception {
+ NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+ }
+
+ @Test
+ public void workingMobileUsbTethering_IPv6() throws Exception {
+ NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+ verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_DualStack() throws Exception {
+ NetworkState upstreamState = buildMobileDualStackUpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mRouterAdvertisementDaemon, times(1)).start();
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+ verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
+ NetworkState upstreamState = buildMobile464xlatUpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+ TEST_XLAT_MOBILE_IFNAME);
+
+ sendIPv6TetherUpdates(upstreamState);
+ verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+ verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
+ // Setup IPv6
+ NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ runUsbTethering(upstreamState);
+
+ // Then 464xlat comes up
+ upstreamState = buildMobile464xlatUpstreamState();
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+ .thenReturn(upstreamState);
+
+ // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
+ mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
+ Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+ 0,
+ upstreamState);
+ mLooper.dispatchAll();
+
+ // Forwarding is added for 464xlat, and was still added only once for v6
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+ verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+ TEST_XLAT_MOBILE_IFNAME);
}
@Test
@@ -420,12 +662,12 @@
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
- mTethering.interfaceStatusChanged(mTestIfname, true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verifyNoMoreInteractions(mConnectivityManager);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
@@ -448,30 +690,23 @@
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
- mTethering.interfaceStatusChanged(mTestIfname, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
verifyInterfaceServingModeStarted();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER);
- // UpstreamNetworkMonitor will be started, and will register two callbacks:
- // a "listen all" and a "track default".
- verify(mConnectivityManager, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
- any(NetworkCallback.class), any(Handler.class));
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_TETHER);
+ verify(mUpstreamNetworkMonitor, times(1)).start();
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
- verify(mConnectivityManager, times(1)).requestNetwork(
- any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
- any(Handler.class));
+ verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
verifyNoMoreInteractions(mConnectivityManager);
@@ -494,14 +729,14 @@
// Emulate externally-visible WifiManager effects, when tethering mode
// is being torn down.
sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mTethering.interfaceRemoved(mTestIfname);
+ mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
mLooper.dispatchAll();
- verify(mNMService, times(1)).untetherInterface(mTestIfname);
+ verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
// TODO: Why is {g,s}etInterfaceConfig() called more than once?
- verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+ verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
verifyNoMoreInteractions(mNMService);
@@ -509,7 +744,7 @@
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
- mTethering.getLastTetherError(mTestIfname));
+ mTethering.getLastTetherError(TEST_WLAN_IFNAME));
}
// TODO: Test with and without interfaceStatusChanged().
@@ -530,21 +765,21 @@
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
// tethering mode is to be started.
- mTethering.interfaceStatusChanged(mTestIfname, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
// We verify get/set called twice here: once for setup and once during
// teardown because all events happen over the course of the single
// dispatchAll() above.
- verify(mNMService, times(2)).getInterfaceConfig(mTestIfname);
+ verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(2))
- .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
- verify(mNMService, times(1)).tetherInterface(mTestIfname);
+ .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
- verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNMService, times(1)).setIpForwardingEnabled(true);
// This never gets called because of the exception thrown above.
@@ -552,9 +787,9 @@
// When the master state machine transitions to an error state it tells
// downstream interfaces, which causes us to tell Wi-Fi about the error
// so it can take down AP mode.
- verify(mNMService, times(1)).untetherInterface(mTestIfname);
+ verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
- mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
verifyNoMoreInteractions(mWifiManager);
verifyNoMoreInteractions(mConnectivityManager);
@@ -596,7 +831,7 @@
@Test
public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
- final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final boolean currDisallow = false;
final boolean nextDisallow = true;
final int expectedInteractionsWithShowNotification = 1;
@@ -618,7 +853,7 @@
@Test
public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
- final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final boolean currDisallow = true;
final boolean nextDisallow = false;
final int expectedInteractionsWithShowNotification = 0;
@@ -629,7 +864,7 @@
@Test
public void testDisallowTetheringUnchanged() throws Exception {
- final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
final int expectedInteractionsWithShowNotification = 0;
boolean currDisallow = true;
boolean nextDisallow = true;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f59850d..e377a47 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -70,6 +70,7 @@
import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.test.filters.SmallTest;
@@ -88,6 +89,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -639,4 +642,32 @@
lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
assertTrue(Vpn.providesRoutesToMostDestinations(lp));
}
+
+ @Test
+ public void testDoesNotLockUpWithTooManyRoutes() {
+ final LinkProperties lp = new LinkProperties();
+ final byte[] ad = new byte[4];
+ // Actually evaluating this many routes under 1500ms is impossible on
+ // current hardware and for some time, as the algorithm is O(n²).
+ // Make sure the system has a safeguard against this and does not
+ // lock up.
+ final int MAX_ROUTES = 4000;
+ final long MAX_ALLOWED_TIME_MS = 1500;
+ for (int i = 0; i < MAX_ROUTES; ++i) {
+ ad[0] = (byte)((i >> 24) & 0xFF);
+ ad[1] = (byte)((i >> 16) & 0xFF);
+ ad[2] = (byte)((i >> 8) & 0xFF);
+ ad[3] = (byte)(i & 0xFF);
+ try {
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.getByAddress(ad), 32)));
+ } catch (UnknownHostException e) {
+ // UnknownHostException is only thrown for an address of illegal length,
+ // which can't happen in the case above.
+ }
+ }
+ final long start = SystemClock.currentThreadTimeMillis();
+ assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+ final long end = SystemClock.currentThreadTimeMillis();
+ assertTrue(end - start < MAX_ALLOWED_TIME_MS);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index db5373a..7c77cf5 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -31,7 +31,6 @@
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
@@ -39,12 +38,12 @@
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.RemoteException;
@@ -75,6 +74,7 @@
@Mock private IControlsTethering mTetherHelper;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
+ @Mock private TetheringDependencies mTetheringDependencies;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -84,7 +84,7 @@
private void initStateMachine(int interfaceType) throws Exception {
mTestedSm = new TetherInterfaceStateMachine(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
- mNMService, mStatsService, mTetherHelper);
+ mNMService, mStatsService, mTetherHelper, mTetheringDependencies);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
@@ -111,7 +111,8 @@
@Test
public void startsOutAvailable() {
mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
- TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper);
+ TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
+ mTetheringDependencies);
mTestedSm.start();
mLooper.dispatchAll();
verify(mTetherHelper).updateInterfaceState(
@@ -346,7 +347,7 @@
* Send a command to the state machine under test, and run the event loop to idle.
*
* @param command One of the TetherInterfaceStateMachine.CMD_* constants.
- * @param obj An additional argument to pass.
+ * @param arg1 An additional argument to pass.
*/
private void dispatchCommand(int command, int arg1) {
mTestedSm.sendMessage(command, arg1);
@@ -371,7 +372,7 @@
*/
private void dispatchTetherConnectionChanged(String upstreamIface) {
mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- upstreamIface);
+ new InterfaceSet(upstreamIface));
mLooper.dispatchAll();
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c3b9def..9661dc2 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -147,6 +147,16 @@
}
@Test
+ public void testCallbacksRegistered() {
+ mUNM.start();
+ verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
+ verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+
+ mUNM.stop();
+ verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
+ }
+
+ @Test
public void testRequestsMobileNetwork() throws Exception {
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 300c701..499f254 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -211,7 +211,7 @@
signature != atoms.signatures.end(); signature++) {
int argIndex;
- fprintf(out, "void\n");
+ fprintf(out, "int\n");
fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -251,7 +251,7 @@
" diff length: %s vs %s\");\n",
attributionDecl.fields.front().name.c_str(),
chainField.name.c_str());
- fprintf(out, " return;\n");
+ fprintf(out, " return -EINVAL;\n");
fprintf(out, " }\n");
}
}
@@ -284,7 +284,7 @@
argIndex++;
}
- fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, " return event.write(LOG_ID_STATS);\n");
fprintf(out, "}\n");
fprintf(out, "\n");
}
@@ -293,7 +293,7 @@
signature != atoms.non_chained_signatures.end(); signature++) {
int argIndex;
- fprintf(out, "void\n");
+ fprintf(out, "int\n");
fprintf(out, "stats_write_non_chained(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -327,7 +327,7 @@
argIndex++;
}
- fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, " return event.write(LOG_ID_STATS);\n");
fprintf(out, "}\n");
fprintf(out, "\n");
}
@@ -377,7 +377,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
- fprintf(out, "void %s(int32_t code ", method_name.c_str());
+ fprintf(out, "int %s(int32_t code ", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -522,7 +522,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
- fprintf(out, " public static native void %s(int code", method_name.c_str());
+ fprintf(out, " public static native int %s(int code", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -719,7 +719,7 @@
result += java_type_signature(*arg);
}
}
- result += ")V";
+ result += ")I";
return result;
}
@@ -732,7 +732,7 @@
signature != signatures.end(); signature++) {
int argIndex;
- fprintf(out, "static void\n");
+ fprintf(out, "static int\n");
fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
jni_function_name(java_method_name, *signature).c_str());
argIndex = 1;
@@ -779,7 +779,7 @@
"\"java/lang/IllegalArgumentException\", "
"\"invalid attribution field(%s) length.\");\n",
chainField.name.c_str());
- fprintf(out, " return;\n");
+ fprintf(out, " return -EINVAL;\n");
fprintf(out, " }\n");
}
if (chainField.javaType == JAVA_TYPE_INT) {
@@ -822,7 +822,7 @@
// stats_write call
argIndex = 1;
- fprintf(out, " android::util::%s(code", cpp_method_name.c_str());
+ fprintf(out, " int ret = android::util::%s(code", cpp_method_name.c_str());
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -868,6 +868,7 @@
}
argIndex++;
}
+ fprintf(out, " return ret;\n");
fprintf(out, "}\n");
fprintf(out, "\n");
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 21ae3a9..b77b1ad 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -25,11 +25,13 @@
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
+import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.BackupUtils;
+import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -76,6 +78,8 @@
/** {@hide} */
private String mPasspointManagementObjectTree;
+ /** {@hide} */
+ private static final int MAXIMUM_RANDOM_MAC_GENERATION_RETRY = 3;
/**
* Recognized key management schemes.
@@ -798,27 +802,37 @@
* @hide
* Randomized MAC address to use with this particular network
*/
+ @NonNull
private MacAddress mRandomizedMacAddress;
/**
* @hide
* Checks if the given MAC address can be used for Connected Mac Randomization
- * by verifying that it is non-null, unicast, and locally assigned.
+ * by verifying that it is non-null, unicast, locally assigned, and not default mac.
* @param mac MacAddress to check
* @return true if mac is good to use
*/
- private boolean isValidMacAddressForRandomization(MacAddress mac) {
- return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned();
+ public static boolean isValidMacAddressForRandomization(MacAddress mac) {
+ return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned()
+ && !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac);
}
/**
* @hide
* Returns Randomized MAC address to use with the network.
- * If it is not set/valid, create a new randomized address.
+ * If it is not set/valid, creates a new randomized address.
+ * If it can't generate a valid mac, returns the default MAC.
*/
- public MacAddress getOrCreateRandomizedMacAddress() {
- if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
+ public @NonNull MacAddress getOrCreateRandomizedMacAddress() {
+ int randomMacGenerationCount = 0;
+ while (!isValidMacAddressForRandomization(mRandomizedMacAddress)
+ && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) {
mRandomizedMacAddress = MacAddress.createRandomUnicastAddress();
+ randomMacGenerationCount++;
+ }
+
+ if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
+ mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
}
return mRandomizedMacAddress;
}
@@ -828,10 +842,7 @@
* Returns MAC address set to be the local randomized MAC address.
* Does not guarantee that the returned address is valid for use.
*/
- public MacAddress getRandomizedMacAddress() {
- if (mRandomizedMacAddress == null) {
- mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
- }
+ public @NonNull MacAddress getRandomizedMacAddress() {
return mRandomizedMacAddress;
}
@@ -839,7 +850,11 @@
* @hide
* @param mac MacAddress to change into
*/
- public void setRandomizedMacAddress(MacAddress mac) {
+ public void setRandomizedMacAddress(@NonNull MacAddress mac) {
+ if (mac == null) {
+ Log.e(TAG, "setRandomizedMacAddress received null MacAddress.");
+ return;
+ }
mRandomizedMacAddress = mac;
}
@@ -1532,7 +1547,7 @@
creatorUid = -1;
shared = true;
dtimInterval = 0;
- mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
+ mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
}
/**
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c8df087..433285b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1317,6 +1317,7 @@
if (pin) {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
NetworkPinner.pin(mContext, request);
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8a3a7f5..3517984 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -18,12 +18,14 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import android.os.Parcel;
import android.net.MacAddress;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiInfo;
import org.junit.Before;
import org.junit.Test;
@@ -176,22 +178,25 @@
@Test
public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
WifiConfiguration config = new WifiConfiguration();
- assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+ MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
+ assertEquals(defaultMac, config.getRandomizedMacAddress());
MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
+ assertNotEquals(defaultMac, firstMacAddress);
assertEquals(firstMacAddress, secondMacAddress);
}
@Test
public void testSetRandomizedMacAddress_ChangesSavedAddress() {
WifiConfiguration config = new WifiConfiguration();
- assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+ MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
+ assertEquals(defaultMac, config.getRandomizedMacAddress());
MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
config.setRandomizedMacAddress(macToChangeInto);
- MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
+ MacAddress macAfterChange = config.getRandomizedMacAddress();
assertEquals(macToChangeInto, macAfterChange);
}
@@ -200,24 +205,37 @@
public void testGetOrCreateRandomizedMacAddress_ReRandomizesInvalidAddress() {
WifiConfiguration config = new WifiConfiguration();
+ MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
MacAddress macAddressZeroes = MacAddress.ALL_ZEROS_ADDRESS;
MacAddress macAddressMulticast = MacAddress.fromString("03:ff:ff:ff:ff:ff");
MacAddress macAddressGlobal = MacAddress.fromString("fc:ff:ff:ff:ff:ff");
config.setRandomizedMacAddress(null);
MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertFalse(macAfterChange.equals(null));
+ assertNotEquals(macAfterChange, null);
+
+ config.setRandomizedMacAddress(defaultMac);
+ macAfterChange = config.getOrCreateRandomizedMacAddress();
+ assertNotEquals(macAfterChange, defaultMac);
config.setRandomizedMacAddress(macAddressZeroes);
macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertFalse(macAfterChange.equals(macAddressZeroes));
+ assertNotEquals(macAfterChange, macAddressZeroes);
config.setRandomizedMacAddress(macAddressMulticast);
macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertFalse(macAfterChange.equals(macAddressMulticast));
+ assertNotEquals(macAfterChange, macAddressMulticast);
config.setRandomizedMacAddress(macAddressGlobal);
macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertFalse(macAfterChange.equals(macAddressGlobal));
+ assertNotEquals(macAfterChange, macAddressGlobal);
+ }
+
+ @Test
+ public void testSetRandomizedMacAddress_DoesNothingWhenNull() {
+ WifiConfiguration config = new WifiConfiguration();
+ MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
+ config.setRandomizedMacAddress(null);
+ assertEquals(defaultMac, config.getRandomizedMacAddress());
}
}