diff options
98 files changed, 4114 insertions, 571 deletions
diff --git a/Android.mk b/Android.mk index 298b85ddf084..866d2a7b0e54 100644 --- a/Android.mk +++ b/Android.mk @@ -781,6 +781,7 @@ LOCAL_PROTOC_FLAGS := \ LOCAL_SOURCE_FILES_ALL_GENERATED := true LOCAL_SRC_FILES := \ cmds/am/proto/instrumentation_data.proto \ + cmds/statsd/src/perfetto/perfetto_config.proto \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto) \ $(call all-proto-files-under, cmds/statsd/src) diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml index bd0b944e1233..132a2f9b2d0c 100644 --- a/apct-tests/perftests/core/AndroidManifest.xml +++ b/apct-tests/perftests/core/AndroidManifest.xml @@ -10,7 +10,11 @@ <application> <uses-library android:name="android.test.runner" /> - <activity android:name="android.perftests.utils.StubActivity" /> + <activity android:name="android.perftests.utils.StubActivity"> + <intent-filter> + <action android:name="com.android.perftests.core.PERFTEST" /> + </intent-filter> + </activity> <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" /> </application> diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java new file mode 100644 index 000000000000..145fbcd2d5d7 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.os; + +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class PackageManagerPerfTest { + private static final String PERMISSION_NAME_EXISTS = + "com.android.perftests.core.TestPermission"; + private static final String PERMISSION_NAME_DOESNT_EXIST = + "com.android.perftests.core.TestBadPermission"; + private static final ComponentName TEST_ACTIVITY = + new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity"); + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void testCheckPermissionExists() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName); + } + } + + @Test + public void testCheckPermissionDoesntExist() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName); + } + } + + @Test + public void testQueryIntentActivities() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final Intent intent = new Intent("com.android.perftests.core.PERFTEST"); + + while (state.keepRunning()) { + pm.queryIntentActivities(intent, 0); + } + } + + @Test + public void testGetPackageInfo() throws Exception { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + pm.getPackageInfo(packageName, 0); + } + } + + @Test + public void testGetApplicationInfo() throws Exception { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + pm.getApplicationInfo(packageName, 0); + } + } + + @Test + public void testGetActivityInfo() throws Exception { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + + while (state.keepRunning()) { + pm.getActivityInfo(TEST_ACTIVITY, 0); + } + } +} diff --git a/apct-tests/perftests/core/src/android/os/PermissionTest.java b/apct-tests/perftests/core/src/android/os/PermissionTest.java deleted file mode 100644 index d292e7dc3b96..000000000000 --- a/apct-tests/perftests/core/src/android/os/PermissionTest.java +++ /dev/null @@ -1,66 +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 android.os; - -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import android.content.Context; -import android.perftests.utils.BenchmarkState; -import android.perftests.utils.PerfStatusReporter; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@LargeTest -public class PermissionTest { - private static final String PERMISSION_HAS_NAME = "com.android.perftests.core.TestPermission"; - private static final String PERMISSION_DOESNT_HAVE_NAME = - "com.android.perftests.core.TestBadPermission"; - private static final String THIS_PACKAGE_NAME = "com.android.perftests.core"; - - @Rule - public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - - @Test - public void testHasPermission() { - final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final Context context = InstrumentationRegistry.getTargetContext(); - while (state.keepRunning()) { - int ret = context.getPackageManager().checkPermission(PERMISSION_HAS_NAME, - THIS_PACKAGE_NAME); - } - } - - @Test - public void testDoesntHavePermission() { - final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final Context context = InstrumentationRegistry.getTargetContext(); - - while (state.keepRunning()) { - int ret = context.getPackageManager().checkPermission(PERMISSION_DOESNT_HAVE_NAME, - THIS_PACKAGE_NAME); - } - } - -} diff --git a/api/current.txt b/api/current.txt index 86f1a3b52583..65bcec7bf233 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2796,6 +2796,7 @@ package android.accessibilityservice { field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6 field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5 field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3 + field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9 field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7 field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService"; field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice"; diff --git a/api/system-current.txt b/api/system-current.txt index f35984aaf8d1..d33b030c12d6 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1777,9 +1777,9 @@ package android.hardware.radio { method public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int); method public long getFirstId(int); method public android.hardware.radio.ProgramSelector.Identifier getPrimaryId(); - method public int getProgramType(); + method public deprecated int getProgramType(); method public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds(); - method public long[] getVendorIds(); + method public deprecated long[] getVendorIds(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector> CREATOR; field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1 @@ -1787,27 +1787,31 @@ package android.hardware.radio { field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8 field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7 field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5 + field public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5 field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa - field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb + field public static final deprecated int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9 field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3 - field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4 + field public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; // 0x2714 + field public static final deprecated int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4 field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0 field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2 field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc - field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf - field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8 - field public static final int PROGRAM_TYPE_AM = 1; // 0x1 - field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3 - field public static final int PROGRAM_TYPE_DAB = 5; // 0x5 - field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6 - field public static final int PROGRAM_TYPE_FM = 2; // 0x2 - field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4 - field public static final int PROGRAM_TYPE_INVALID = 0; // 0x0 - field public static final int PROGRAM_TYPE_SXM = 7; // 0x7 - field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf - field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8 + field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf + field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf + field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8 + field public static final int IDENTIFIER_TYPE_VENDOR_START = 1000; // 0x3e8 + field public static final deprecated int PROGRAM_TYPE_AM = 1; // 0x1 + field public static final deprecated int PROGRAM_TYPE_AM_HD = 3; // 0x3 + field public static final deprecated int PROGRAM_TYPE_DAB = 5; // 0x5 + field public static final deprecated int PROGRAM_TYPE_DRMO = 6; // 0x6 + field public static final deprecated int PROGRAM_TYPE_FM = 2; // 0x2 + field public static final deprecated int PROGRAM_TYPE_FM_HD = 4; // 0x4 + field public static final deprecated int PROGRAM_TYPE_INVALID = 0; // 0x0 + field public static final deprecated int PROGRAM_TYPE_SXM = 7; // 0x7 + field public static final deprecated int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf + field public static final deprecated int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8 } public static final class ProgramSelector.Identifier implements android.os.Parcelable { @@ -1822,7 +1826,7 @@ package android.hardware.radio { public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation { } - public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation { + public static abstract deprecated class ProgramSelector.ProgramType implements java.lang.annotation.Annotation { } public class RadioManager { @@ -2860,9 +2864,18 @@ package android.net { method public void onTetheringStarted(); } + public final class IpSecManager { + method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; + } + + public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable { + method public void close(); + method public java.lang.String getInterfaceName(); + } + public static class IpSecTransform.Builder { + method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; method public android.net.IpSecTransform.Builder setNattKeepalive(int); - method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network); } public class NetworkKey implements android.os.Parcelable { diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h index c41612de4eb3..166796673e25 100644 --- a/cmds/incident_helper/src/TextParserBase.h +++ b/cmds/incident_helper/src/TextParserBase.h @@ -68,4 +68,4 @@ public: virtual status_t Parse(const int in, const int out) const; }; -#endif // TEXT_PARSER_BASE_H
\ No newline at end of file +#endif // TEXT_PARSER_BASE_H diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md index ad0fa08c7326..71c6deb18aac 100644 --- a/cmds/incidentd/README.md +++ b/cmds/incidentd/README.md @@ -12,8 +12,8 @@ Run the test on a device manually ``` root$ mmm -j frameworks/base/cmds/incidentd && \ -adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/incidentd_test/ && \ -adb shell /data/nativetest/incidentd_test/incidentd_test 2>/dev/null +adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/ && \ +adb shell /data/nativetest/incidentd_test 2>/dev/null ``` Run the test via AndroidTest.xml diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc index 66667dca2982..1bd146850ea9 100644 --- a/cmds/incidentd/incidentd.rc +++ b/cmds/incidentd/incidentd.rc @@ -19,4 +19,4 @@ service incidentd /system/bin/incidentd on post-fs-data # Create directory for incidentd - mkdir /data/misc/incidents 0770 root root + mkdir /data/misc/incidents 0770 incidentd incidentd diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index 30dd339a629b..0fff4e6dc4a0 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -63,12 +63,14 @@ FdBuffer::read(int fd, int64_t timeout) int64_t remainingTime = (mStartTime + timeout) - uptimeMillis(); if (remainingTime <= 0) { + if (DEBUG) ALOGD("timed out due to long read"); mTimedOut = true; break; } int count = poll(&pfds, 1, remainingTime); if (count == 0) { + if (DEBUG) ALOGD("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { @@ -129,6 +131,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis(); if (remainingTime <= 0) { + if (DEBUG) ALOGD("timed out due to long read"); mTimedOut = true; break; } @@ -136,6 +139,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou // wait for any pfds to be ready to perform IO int count = poll(pfds, 3, remainingTime); if (count == 0) { + if (DEBUG) ALOGD("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index c4b54bbbc022..a97eb861578e 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -43,8 +43,9 @@ String16 const DUMP_PERMISSION("android.permission.DUMP"); String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS"); static Status -checkIncidentPermissions() +checkIncidentPermissions(const IncidentReportArgs& args) { + // checking calling permission. if (!checkCallingPermission(DUMP_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP", IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); @@ -57,10 +58,24 @@ checkIncidentPermissions() return Status::fromExceptionCode(Status::EX_SECURITY, "Calling process does not have permission: android.permission.USAGE_STATS"); } + + // checking calling request uid permission. + uid_t callingUid = IPCThreadState::self()->getCallingUid(); + switch (args.dest()) { + case DEST_LOCAL: + if (callingUid != AID_SHELL || callingUid != AID_ROOT) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Calling process does not have permission to get local data."); + } + case DEST_EXPLICIT: + if (callingUid != AID_SHELL || callingUid != AID_ROOT || + callingUid != AID_STATSD || callingUid != AID_SYSTEM) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Calling process does not have permission to get explicit data."); + } + } return Status::ok(); } - - // ================================================================================ ReportRequestQueue::ReportRequestQueue() { @@ -71,7 +86,7 @@ ReportRequestQueue::~ReportRequestQueue() } void -ReportRequestQueue::addRequest(const sp<ReportRequest>& request) +ReportRequestQueue::addRequest(const sp<ReportRequest>& request) { unique_lock<mutex> lock(mLock); mQueue.push_back(request); @@ -196,7 +211,7 @@ IncidentService::reportIncident(const IncidentReportArgs& args) { ALOGI("reportIncident"); - Status status = checkIncidentPermissions(); + Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } @@ -212,7 +227,7 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args, { ALOGI("reportIncidentToStream"); - Status status = checkIncidentPermissions(); + Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index 34930aa57321..bd559d6980f1 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -251,7 +251,7 @@ Reporter::create_file(int* fd) // Override umask. Not super critical. If it fails go on with life. chmod(filename, 0660); - if (chown(filename, AID_SYSTEM, AID_SYSTEM)) { + if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) { ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno)); status_t err = -errno; unlink(mFilename.c_str()); diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 61d16f815e65..0827785811b6 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -19,6 +19,7 @@ #include "Section.h" #include <errno.h> +#include <sys/prctl.h> #include <unistd.h> #include <wait.h> @@ -30,7 +31,6 @@ #include <log/log_event_list.h> #include <log/logprint.h> #include <log/log_read.h> -#include <private/android_filesystem_config.h> // for AID_NOBODY #include <private/android_logger.h> #include "FdBuffer.h" @@ -55,26 +55,20 @@ static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe) { const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL }; - // fork used in multithreaded environment, avoid adding unnecessary code in child process pid_t pid = fork(); if (pid == 0) { - // child process executes incident helper as nobody - if (setgid(AID_NOBODY) == -1) { - ALOGW("%s can't change gid: %s", name, strerror(errno)); - _exit(EXIT_FAILURE); - } - if (setuid(AID_NOBODY) == -1) { - ALOGW("%s can't change uid: %s", name, strerror(errno)); - _exit(EXIT_FAILURE); - } - - if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close() || - dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) { + if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 + || !p2cPipe.close() + || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 + || !c2pPipe.close()) { ALOGW("%s can't setup stdin and stdout for incident helper", name); _exit(EXIT_FAILURE); } + /* make sure the child dies when incidentd dies */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + execv(INCIDENT_HELPER, const_cast<char**>(ihArgs)); ALOGW("%s failed in incident helper process: %s", name, strerror(errno)); @@ -87,11 +81,23 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi } // ================================================================================ +static status_t statusCode(int status) { + if (WIFSIGNALED(status)) { + ALOGD("return by signal: %s", strerror(WTERMSIG(status))); + return -WTERMSIG(status); + } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { + ALOGD("return by exit: %s", strerror(WEXITSTATUS(status))); + return -WEXITSTATUS(status); + } + return NO_ERROR; +} + static status_t kill_child(pid_t pid) { int status; + ALOGD("try to kill child process %d", pid); kill(pid, SIGKILL); if (waitpid(pid, &status, 0) == -1) return -1; - return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status); + return statusCode(status); } static status_t wait_child(pid_t pid) { @@ -104,7 +110,7 @@ static status_t wait_child(pid_t pid) { nanosleep(&WAIT_INTERVAL_NS, NULL); } if (!died) return kill_child(pid); - return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status); + return statusCode(status); } // ================================================================================ static const Privacy* @@ -275,9 +281,9 @@ FileSection::Execute(ReportRequestSet* requests) const status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(), this->timeoutMs, mIsSysfs); if (readStatus != NO_ERROR || buffer.timedOut()) { - ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false", - strerror(-kill_child(pid))); + ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s", + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + kill_child(pid); return readStatus; } @@ -543,10 +549,10 @@ CommandSection::Execute(ReportRequestSet* requests) const close(cmdPipe.writeFd()); status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs); if (readStatus != NO_ERROR || buffer.timedOut()) { - ALOGW("CommandSection '%s' failed to read data from incident helper: %s, " - "timedout: %s, kill command: %s, kill incident helper: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false", - strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid))); + ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s", + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + kill_child(cmdPid); + kill_child(ihPid); return readStatus; } diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp index af4a35cc0015..90f543e30ff7 100644 --- a/cmds/incidentd/src/io_util.cpp +++ b/cmds/incidentd/src/io_util.cpp @@ -23,7 +23,7 @@ status_t write_all(int fd, uint8_t const* buf, size_t size) { while (size > 0) { - ssize_t amt = ::write(fd, buf, size); + ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size)); if (amt < 0) { return -errno; } diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp index 65030b3a1799..20111d8ae89a 100644 --- a/cmds/incidentd/src/report_directory.cpp +++ b/cmds/incidentd/src/report_directory.cpp @@ -58,26 +58,9 @@ create_directory(const char* directory) goto done; } } else { - if (mkdir(dir, 0770)) { - ALOGE("No incident reports today. " - "Unable to create incident report dir %s: %s", dir, - strerror(errno)); - err = -errno; - goto done; - } - if (chmod(dir, 0770)) { - ALOGE("No incident reports today. " - "Unable to set permissions for incident report dir %s: %s", dir, - strerror(errno)); - err = -errno; - goto done; - } - if (chown(dir, AID_SYSTEM, AID_SYSTEM)) { - ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n", - dir, strerror(errno)); - err = -errno; - goto done; - } + ALOGE("No such directory %s, something wrong.", dir); + err = -1; + goto done; } if (!last) { *d++ = '/'; @@ -97,8 +80,7 @@ create_directory(const char* directory) err = BAD_VALUE; goto done; } - if ((st.st_uid != AID_SYSTEM && st.st_uid != AID_ROOT) || - (st.st_gid != AID_SYSTEM && st.st_gid != AID_ROOT)) { + if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) { ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", st.st_uid, st.st_gid, directory); err = BAD_VALUE; diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 5eff54887be1..01f4a84bbb93 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -34,6 +34,7 @@ statsd_common_src := \ src/config/ConfigKey.cpp \ src/config/ConfigListener.cpp \ src/config/ConfigManager.cpp \ + src/external/Perfetto.cpp \ src/external/StatsPuller.cpp \ src/external/StatsCompanionServicePuller.cpp \ src/external/SubsystemSleepStatePuller.cpp \ @@ -57,6 +58,7 @@ statsd_common_src := \ src/metrics/MetricsManager.cpp \ src/metrics/metrics_manager_util.cpp \ src/packages/UidMap.cpp \ + src/perfetto/perfetto_config.proto \ src/storage/StorageManager.cpp \ src/StatsLogProcessor.cpp \ src/StatsService.cpp \ @@ -209,6 +211,7 @@ LOCAL_MODULE := statsdprotolite LOCAL_SRC_FILES := \ src/stats_log.proto \ src/statsd_config.proto \ + src/perfetto/perfetto_config.proto \ src/atoms.proto LOCAL_PROTOC_OPTIMIZE_TYPE := lite diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index f10b2cf618cd..9b65acf86604 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "external/Perfetto.h" #include "guardrail/StatsdStats.h" #include <android/os/IIncidentManager.h> @@ -239,7 +240,7 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { } break; case Subscription::SubscriberInformationCase::kPerfettoDetails: - ALOGW("Perfetto reports not implemented."); + CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details()); break; default: break; diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp index a61afb429f54..a75127324745 100644 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp @@ -34,7 +34,7 @@ namespace android { namespace os { namespace statsd { -static const string sProcFile = "/proc/uid_cputime/show_uid_stat"; +static const string sProcFile = "/proc/uid_time_in_state"; static const int kLineBufferSize = 1024; /** diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp new file mode 100644 index 000000000000..f7b33e77c557 --- /dev/null +++ b/cmds/statsd/src/external/Perfetto.cpp @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include "Log.h" + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert + +#include <android-base/unique_fd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> + +namespace { +const char kDropboxTag[] = "perfetto"; +} + +namespace android { +namespace os { +namespace statsd { + +bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) { + ALOGD("Starting trace collection through perfetto"); + + if (!config.has_trace_config()) { + ALOGE("The perfetto trace config is empty, aborting"); + return false; + } + + android::base::unique_fd readPipe; + android::base::unique_fd writePipe; + if (!android::base::Pipe(&readPipe, &writePipe)) { + ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno)); + return false; + } + + pid_t pid = fork(); + if (pid < 0) { + ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno)); + return false; + } + + if (pid == 0) { + // Child process. + + // No malloc calls or library calls after this point. Remember that even + // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf(). + + writePipe.reset(); // Close the write end (owned by the main process). + + // Replace stdin with |readPipe| so the main process can write into it. + if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1); + execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox", + kDropboxTag, nullptr); + + // execl() doesn't return in case of success, if we get here something failed. + _exit(1); + } + + // Main process. + + readPipe.reset(); // Close the read end (owned by the child process). + + // Using fopen() because fwrite() has the right logic to chunking write() + // over a pipe (see __sfvwrite()). + FILE* writePipeStream = fdopen(writePipe.get(), "wb"); + if (!writePipeStream) { + ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno)); + return false; + } + + std::string cfgProto = config.trace_config().SerializeAsString(); + size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream); + fclose(writePipeStream); + if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) { + ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten, + strerror(errno)); + return false; + } + + // This does NOT wait for the full duration of the trace. It just waits until the process + // has read the config from stdin and detached. + int childStatus = 0; + waitpid(pid, &childStatus, 0); + if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) { + ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus); + return false; + } + + ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded"); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h new file mode 100644 index 000000000000..e2e02533f17f --- /dev/null +++ b/cmds/statsd/src/external/Perfetto.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#pragma once + +using android::os::StatsLogEventWrapper; + +namespace android { +namespace os { +namespace statsd { + +class PerfettoDetails; // Declared in statsd_config.pb.h + +// Starts the collection of a Perfetto trace with the given |config|. +// The trace is uploaded to Dropbox by the perfetto cmdline util once done. +// This method returns immediately after passing the config and does NOT wait +// for the full duration of the trace. +bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index c7550f7d4711..e98587356857 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -285,8 +285,15 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( interval.start = value; interval.startUpdated = true; } else { + // Generally we expect value to be monotonically increasing. + // If not, there was a reset event. We take the absolute value as + // diff in this case. if (interval.startUpdated) { - interval.sum += (value - interval.start); + if (value > interval.start) { + interval.sum += (value - interval.start); + } else { + interval.sum += value; + } interval.startUpdated = false; } else { VLOG("No start for matching end %ld", value); diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto new file mode 100644 index 000000000000..dc868f997a63 --- /dev/null +++ b/cmds/statsd/src/perfetto/perfetto_config.proto @@ -0,0 +1,61 @@ +/* + * 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. + */ + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + +package perfetto.protos; + +message DataSourceConfig { + message FtraceConfig { + repeated string event_names = 1; + } + + optional string name = 1; + + optional uint32 target_buffer = 2; + + optional FtraceConfig ftrace_config = 100; +} + +message TraceConfig { + message BufferConfig { + optional uint32 size_kb = 1; + + enum OptimizeFor { + DEFAULT = 0; + ONE_SHOT_READ = 1; + + } + optional OptimizeFor optimize_for = 3; + + enum FillPolicy { + UNSPECIFIED = 0; + RING_BUFFER = 1; + } + optional FillPolicy fill_policy = 4; + } + repeated BufferConfig buffers = 1; + + message DataSource { + optional protos.DataSourceConfig config = 1; + + repeated string producer_name_filter = 2; + } + repeated DataSource data_sources = 2; + + optional uint32 duration_ms = 3; +} diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 624785486043..cd60ee74e3ad 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -22,6 +22,8 @@ package android.os.statsd; option java_package = "com.android.internal.os"; option java_outer_classname = "StatsdConfigProto"; +import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto"; + enum Position { POSITION_UNKNOWN = 0; FIRST = 1; @@ -272,7 +274,7 @@ message IncidentdDetails { } message PerfettoDetails { - optional int32 perfetto_stuff = 1; + optional perfetto.protos.TraceConfig trace_config = 1; } message Subscription { diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index a4c080063284..73fbaa4e2ad0 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -21,9 +21,11 @@ LOCAL_PACKAGE_NAME := StatsdDogfood LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += ../../src/stats_log.proto \ ../../src/atoms.proto \ + ../../src/perfetto/perfetto_config.proto \ ../../src/statsd_config.proto LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/ + LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk index 0a0fd6647fbb..f5722c2c3d19 100644 --- a/cmds/statsd/tools/loadtest/Android.mk +++ b/cmds/statsd/tools/loadtest/Android.mk @@ -21,6 +21,7 @@ LOCAL_PACKAGE_NAME := StatsdLoadtest LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += ../../src/stats_log.proto \ ../../src/atoms.proto \ + ../../src/perfetto/perfetto_config.proto \ ../../src/statsd_config.proto LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 97dcb90bbb99..0a4541ba4777 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -363,6 +363,11 @@ public abstract class AccessibilityService extends Service { */ public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; + /** + * Action to take a screenshot + */ + public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; + private static final String LOG_TAG = "AccessibilityService"; /** diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 3556751f4af4..0cf7605c98a2 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -59,24 +59,56 @@ import java.util.stream.Stream; */ @SystemApi public final class ProgramSelector implements Parcelable { + /** Invalid program type. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_INVALID = 0; - /** Analogue AM radio (with or without RDS). */ + /** Analogue AM radio (with or without RDS). + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_AM = 1; - /** analogue FM radio (with or without RDS). */ + /** analogue FM radio (with or without RDS). + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_FM = 2; - /** AM HD Radio. */ + /** AM HD Radio. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_AM_HD = 3; - /** FM HD Radio. */ + /** FM HD Radio. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_FM_HD = 4; - /** Digital audio broadcasting. */ + /** Digital audio broadcasting. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_DAB = 5; - /** Digital Radio Mondiale. */ + /** Digital Radio Mondiale. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_DRMO = 6; - /** SiriusXM Satellite Radio. */ + /** SiriusXM Satellite Radio. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_SXM = 7; - /** Vendor-specific, not synced across devices. */ + /** Vendor-specific, not synced across devices. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_VENDOR_START = 1000; + /** @deprecated use {@link ProgramIdentifier} instead */ + @Deprecated public static final int PROGRAM_TYPE_VENDOR_END = 1999; + /** @deprecated use {@link ProgramIdentifier} instead */ + @Deprecated @IntDef(prefix = { "PROGRAM_TYPE_" }, value = { PROGRAM_TYPE_INVALID, PROGRAM_TYPE_AM, @@ -112,18 +144,46 @@ public final class ProgramSelector implements Parcelable { * * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS), * as opposed to HD Radio standard (where it's 1-based). + * + * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead */ + @Deprecated public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; /** - * 24bit compound primary identifier for DAB. + * 64bit additional identifier for HD Radio. + * + * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not + * globally unique. To provide a best-effort solution, a short version of + * station name may be carried as additional identifier and may be used + * by the tuner hardware to double-check tuning. + * + * The name is limited to the first 8 A-Z0-9 characters (lowercase letters + * must be converted to uppercase). Encoded in little-endian ASCII: + * the first character of the name is the LSB. + * + * For example: "Abc" is encoded as 0x434241. + */ + public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; + /** + * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT} + */ + public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; + /** + * 28bit compound primary identifier for Digital Audio Broadcasting. * * Consists of (from the LSB): * - 16bit: SId; - * - 8bit: ECC code. + * - 8bit: ECC code; + * - 4bit: SCIdS. + * + * SCIdS (Service Component Identifier within the Service) value + * of 0 represents the main service, while 1 and above represents + * secondary services. + * * The remaining bits should be set to zeros when writing on the chip side * and ignored when read. */ - public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; + public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC; /** 16bit */ public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6; /** 12bit */ @@ -134,7 +194,11 @@ public final class ProgramSelector implements Parcelable { public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; /** kHz */ public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; - /** 1: AM, 2:FM */ + /** + * 1: AM, 2:FM + * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead + */ + @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; /** 32bit */ public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; @@ -148,14 +212,29 @@ public final class ProgramSelector implements Parcelable { * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must * not be used in any program type other than 1015). */ - public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START; - public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END; + public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START; + /** + * @see {@link IDENTIFIER_TYPE_VENDOR_START} + */ + public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END; + /** + * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead + */ + @Deprecated + public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START; + /** + * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead + */ + @Deprecated + public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END; @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = { IDENTIFIER_TYPE_INVALID, IDENTIFIER_TYPE_AMFM_FREQUENCY, IDENTIFIER_TYPE_RDS_PI, IDENTIFIER_TYPE_HD_STATION_ID_EXT, IDENTIFIER_TYPE_HD_SUBCHANNEL, + IDENTIFIER_TYPE_HD_STATION_NAME, + IDENTIFIER_TYPE_DAB_SID_EXT, IDENTIFIER_TYPE_DAB_SIDECC, IDENTIFIER_TYPE_DAB_ENSEMBLE, IDENTIFIER_TYPE_DAB_SCID, @@ -166,7 +245,7 @@ public final class ProgramSelector implements Parcelable { IDENTIFIER_TYPE_SXM_SERVICE_ID, IDENTIFIER_TYPE_SXM_CHANNEL, }) - @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END) + @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END) @Retention(RetentionPolicy.SOURCE) public @interface IdentifierType {} @@ -205,7 +284,9 @@ public final class ProgramSelector implements Parcelable { * Type of a radio technology. * * @return program type. + * @deprecated use {@link getPrimaryId} instead */ + @Deprecated public @ProgramType int getProgramType() { return mProgramType; } @@ -273,7 +354,10 @@ public final class ProgramSelector implements Parcelable { * preserving elements order. * * @return an array of vendor identifiers, must not be modified. + * @deprecated for HAL 1.x compatibility; + * HAL 2.x uses standard primary/secondary lists for vendor IDs */ + @Deprecated public @NonNull long[] getVendorIds() { return mVendorIds; } @@ -427,6 +511,10 @@ public final class ProgramSelector implements Parcelable { private final long mValue; public Identifier(@IdentifierType int type, long value) { + if (type == IDENTIFIER_TYPE_HD_STATION_NAME) { + // see getType + type = IDENTIFIER_TYPE_HD_SUBCHANNEL; + } mType = type; mValue = value; } @@ -437,6 +525,13 @@ public final class ProgramSelector implements Parcelable { * @return type of an identifier. */ public @IdentifierType int getType() { + if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) { + /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ + * in possible values: sub channel is 0-7, station name is greater than ASCII space + * code (32). + */ + return IDENTIFIER_TYPE_HD_STATION_NAME; + } return mType; } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 2cda58c99a61..f04f03f6b617 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; @@ -625,6 +626,133 @@ public final class IpSecManager { } /** + * This class represents an IpSecTunnelInterface + * + * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as + * local endpoints for IPsec tunnels. + * + * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be + * applied to provide IPsec security to packets sent through the tunnel. While a tunnel + * cannot be used in standalone mode within Android, the higher layers may use the tunnel + * to create Network objects which are accessible to the Android system. + * @hide + */ + @SystemApi + public static final class IpSecTunnelInterface implements AutoCloseable { + private final IIpSecService mService; + private final InetAddress mRemoteAddress; + private final InetAddress mLocalAddress; + private final Network mUnderlyingNetwork; + private final CloseGuard mCloseGuard = CloseGuard.get(); + private String mInterfaceName; + private int mResourceId = INVALID_RESOURCE_ID; + + /** Get the underlying SPI held by this object. */ + public String getInterfaceName() { + return mInterfaceName; + } + + /** + * Add an address to the IpSecTunnelInterface + * + * <p>Add an address which may be used as the local inner address for + * tunneled traffic. + * + * @param address the local address for traffic inside the tunnel + * @throws IOException if the address could not be added + * @hide + */ + public void addAddress(LinkAddress address) throws IOException { + } + + /** + * Remove an address from the IpSecTunnelInterface + * + * <p>Remove an address which was previously added to the IpSecTunnelInterface + * + * @param address to be removed + * @throws IOException if the address could not be removed + * @hide + */ + public void removeAddress(LinkAddress address) throws IOException { + } + + private IpSecTunnelInterface(@NonNull IIpSecService service, + @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, + @NonNull Network underlyingNetwork) + throws ResourceUnavailableException, IOException { + mService = service; + mLocalAddress = localAddress; + mRemoteAddress = remoteAddress; + mUnderlyingNetwork = underlyingNetwork; + // TODO: Call IpSecService + } + + /** + * Delete an IpSecTunnelInterface + * + * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system + * resources. Any packets bound for this interface either inbound or outbound will + * all be lost. + */ + @Override + public void close() { + // try { + // TODO: Call IpSecService + mResourceId = INVALID_RESOURCE_ID; + // } catch (RemoteException e) { + // throw e.rethrowFromSystemServer(); + // } + mCloseGuard.close(); + } + + /** Check that the Interface was closed properly. */ + @Override + protected void finalize() throws Throwable { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + } + + /** + * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic. + * + * @param localAddress The local addres of the tunnel + * @param remoteAddress The local addres of the tunnel + * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. + * This network should almost certainly be a network such as WiFi with an L2 address. + * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties + * @throws IOException indicating that the socket could not be opened or bound + * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open + * @hide + */ + @SystemApi + public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, + @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) + throws ResourceUnavailableException, IOException { + return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork); + } + + /** + * Apply a transform to the IpSecTunnelInterface + * + * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied + * transform. + * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which + * the transform will be used. + * @param transform an {@link IpSecTransform} created in tunnel mode + * @throws IOException indicating that the transform could not be applied due to a lower + * layer failure. + * @hide + */ + @SystemApi + void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction, + IpSecTransform transform) throws IOException { + // TODO: call IpSecService + } + /** * Construct an instance of IpSecManager within an application context. * * @param context the application context for this manager diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 7b9b4830929d..be6026ff376e 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -300,21 +300,6 @@ public final class IpSecTransform implements AutoCloseable { } /** - * Set the {@link Network} which will carry tunneled traffic. - * - * <p>Restricts the transformed traffic to a particular {@link Network}. This is required - * for tunnel mode, otherwise tunneled traffic would be sent on the default network. - * - * @hide - */ - @SystemApi - public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) { - Preconditions.checkNotNull(net); - mConfig.setNetwork(net); - return this; - } - - /** * Add UDP encapsulation to an IPv4 transform. * * <p>This allows IPsec traffic to pass through a NAT. @@ -415,6 +400,7 @@ public final class IpSecTransform implements AutoCloseable { * @throws IOException indicating other errors * @hide */ + @SystemApi public IpSecTransform buildTunnelModeTransform( @NonNull InetAddress sourceAddress, @NonNull IpSecManager.SecurityParameterIndex spi) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index dc271d8639d5..49879a8a84a7 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -35,6 +35,7 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -683,6 +684,14 @@ public abstract class BatteryStats implements Parcelable { public abstract long[] getCpuFreqTimes(int which); public abstract long[] getScreenOffCpuFreqTimes(int which); + /** + * Returns cpu active time of an uid. + */ + public abstract long getCpuActiveTime(); + /** + * Returns cpu times of an uid on each cluster + */ + public abstract long[] getCpuClusterTimes(); /** * Returns cpu times of an uid at a particular process state. @@ -1500,6 +1509,10 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4; public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK = 0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT; + // Values for NUM_GPS_SIGNAL_QUALITY_LEVELS + public static final int STATE2_GPS_SIGNAL_QUALITY_SHIFT = 7; + public static final int STATE2_GPS_SIGNAL_QUALITY_MASK = + 0x1 << STATE2_GPS_SIGNAL_QUALITY_SHIFT; public static final int STATE2_POWER_SAVE_FLAG = 1<<31; public static final int STATE2_VIDEO_ON_FLAG = 1<<30; @@ -2088,6 +2101,23 @@ public abstract class BatteryStats implements Parcelable { */ public abstract int getNumConnectivityChange(int which); + + /** + * Returns the time in microseconds that the phone has been running with + * the given GPS signal quality level + * + * {@hide} + */ + public abstract long getGpsSignalQualityTime(int strengthBin, + long elapsedRealtimeUs, int which); + + /** + * Returns the GPS battery drain in mA-ms + * + * {@hide} + */ + public abstract long getGpsBatteryDrainMaMs(); + /** * Returns the time in microseconds that the phone has been on while the device was * running on battery. @@ -2312,6 +2342,9 @@ public abstract class BatteryStats implements Parcelable { WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES), new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"), new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"), + new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK, + HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss", + new String[] { "poor", "good"}, new String[] { "poor", "good"}), }; public static final String[] HISTORY_EVENT_NAMES = new String[] { @@ -4732,6 +4765,43 @@ public abstract class BatteryStats implements Parcelable { pw.print(prefix); sb.setLength(0); sb.append(prefix); + sb.append(" GPS Statistics:"); + pw.println(sb.toString()); + + sb.setLength(0); + sb.append(prefix); + sb.append(" GPS signal quality (Top 4 Average CN0):"); + final String[] gpsSignalQualityDescription = new String[]{ + "poor (less than 20 dBHz): ", + "good (greater than 20 dBHz): "}; + final int numGpsSignalQualityBins = Math.min(GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS, + gpsSignalQualityDescription.length); + for (int i=0; i<numGpsSignalQualityBins; i++) { + final long time = getGpsSignalQualityTime(i, rawRealtime, which); + sb.append("\n "); + sb.append(prefix); + sb.append(" "); + sb.append(gpsSignalQualityDescription[i]); + formatTimeMs(sb, time/1000); + sb.append("("); + sb.append(formatRatioLocked(time, whichBatteryRealtime)); + sb.append(") "); + } + pw.println(sb.toString()); + + final long gpsBatteryDrainMaMs = getGpsBatteryDrainMaMs(); + if (gpsBatteryDrainMaMs > 0) { + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); + sb.append(" Battery Drain (mAh): "); + sb.append(Double.toString(((double) gpsBatteryDrainMaMs)/(3600 * 1000))); + pw.println(sb.toString()); + } + + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); sb.append(" CONNECTIVITY POWER SUMMARY END"); pw.println(sb.toString()); pw.println(""); diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl index 5e8590af11f1..3de953a2dfbe 100644 --- a/core/java/android/os/IPermissionController.aidl +++ b/core/java/android/os/IPermissionController.aidl @@ -22,4 +22,5 @@ interface IPermissionController { boolean checkPermission(String permission, int pid, int uid); String[] getPackagesForUid(int uid); boolean isRuntimePermission(String permission); + int getPackageUid(String packageName, int flags); } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index e6cf5e6a6cbf..7654e9b6ee22 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -151,6 +151,12 @@ public class Process { */ public static final int OTA_UPDATE_UID = 1061; + /** + * Defines the UID used for incidentd. + * @hide + */ + public static final int INCIDENTD_UID = 1067; + /** {@hide} */ public static final int NOBODY_UID = 9999; diff --git a/core/java/android/os/connectivity/GpsBatteryStats.aidl b/core/java/android/os/connectivity/GpsBatteryStats.aidl new file mode 100644 index 000000000000..7b96d1a8e062 --- /dev/null +++ b/core/java/android/os/connectivity/GpsBatteryStats.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.connectivity; + +/** {@hide} */ +parcelable GpsBatteryStats;
\ No newline at end of file diff --git a/core/java/android/os/connectivity/GpsBatteryStats.java b/core/java/android/os/connectivity/GpsBatteryStats.java new file mode 100644 index 000000000000..f2ac5ef6d40b --- /dev/null +++ b/core/java/android/os/connectivity/GpsBatteryStats.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os.connectivity; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.location.gnssmetrics.GnssMetrics; + +import java.util.Arrays; + +/** + * API for GPS power stats + * + * @hide + */ +public final class GpsBatteryStats implements Parcelable { + + private long mLoggingDurationMs; + private long mEnergyConsumedMaMs; + private long[] mTimeInGpsSignalQualityLevel; + + public static final Parcelable.Creator<GpsBatteryStats> CREATOR = new + Parcelable.Creator<GpsBatteryStats>() { + public GpsBatteryStats createFromParcel(Parcel in) { + return new GpsBatteryStats(in); + } + + public GpsBatteryStats[] newArray(int size) { + return new GpsBatteryStats[size]; + } + }; + + public GpsBatteryStats() { + initialize(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mLoggingDurationMs); + out.writeLong(mEnergyConsumedMaMs); + out.writeLongArray(mTimeInGpsSignalQualityLevel); + } + + public void readFromParcel(Parcel in) { + mLoggingDurationMs = in.readLong(); + mEnergyConsumedMaMs = in.readLong(); + in.readLongArray(mTimeInGpsSignalQualityLevel); + } + + public long getLoggingDurationMs() { + return mLoggingDurationMs; + } + + public long getEnergyConsumedMaMs() { + return mEnergyConsumedMaMs; + } + + public long[] getTimeInGpsSignalQualityLevel() { + return mTimeInGpsSignalQualityLevel; + } + + public void setLoggingDurationMs(long t) { + mLoggingDurationMs = t; + return; + } + + public void setEnergyConsumedMaMs(long e) { + mEnergyConsumedMaMs = e; + return; + } + + public void setTimeInGpsSignalQualityLevel(long[] t) { + mTimeInGpsSignalQualityLevel = Arrays.copyOfRange(t, 0, + Math.min(t.length, GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS)); + return; + } + + @Override + public int describeContents() { + return 0; + } + + private GpsBatteryStats(Parcel in) { + initialize(); + readFromParcel(in); + } + + private void initialize() { + mLoggingDurationMs = 0; + mEnergyConsumedMaMs = 0; + mTimeInGpsSignalQualityLevel = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; + return; + } +}
\ No newline at end of file diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8c2a8ae9080a..b2cc18b94641 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -16,10 +16,12 @@ package android.provider; +import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR; import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR; import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR; import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR; import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR; +import static android.provider.SettingsValidators.LOCALE_VALIDATOR; import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR; import static android.provider.SettingsValidators.URI_VALIDATOR; @@ -4008,6 +4010,9 @@ public final class Settings { * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * + * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator, + * otherwise they won't be restored. + * * @hide */ public static final String[] LEGACY_RESTORE_SETTINGS = { @@ -4116,6 +4121,9 @@ public final class Settings { /** * These are all public system settings * + * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator, + * otherwise they won't be restored. + * * @hide */ public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); @@ -4391,8 +4399,8 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON; - private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = - BOOLEAN_VALIDATOR; + private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = + BOOLEAN_VALIDATOR; /** * @deprecated Use @@ -4402,6 +4410,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY; + private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT} * instead @@ -4409,6 +4420,9 @@ public final class Settings { @Deprecated public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT; + private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} instead */ @@ -5190,6 +5204,8 @@ public final class Settings { @Deprecated public static final String ALLOW_MOCK_LOCATION = "mock_location"; + private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR; + /** * On Android 8.0 (API level 26) and higher versions of the platform, * a 64-bit number (expressed as a hexadecimal string), unique to @@ -5284,6 +5300,8 @@ public final class Settings { @TestApi public static final String AUTOFILL_SERVICE = "autofill_service"; + private static final Validator AUTOFILL_SERVICE_VALIDATOR = COMPONENT_NAME_VALIDATOR; + /** * Boolean indicating if Autofill supports field classification. * @@ -5370,9 +5388,38 @@ public final class Settings { * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. + * + * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0" + * where imeId is ComponentName and subtype is int32. */ public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; + private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] inputMethods = value.split(":"); + boolean valid = true; + for (String inputMethod : inputMethods) { + if (inputMethod.length() == 0) { + return false; + } + String[] subparts = inputMethod.split(";"); + for (String subpart : subparts) { + // allow either a non negative integer or a ComponentName + valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart) + || COMPONENT_NAME_VALIDATOR.validate(subpart)); + } + if (!valid) { + return false; + } + } + return valid; + } + }; + /** * List of system input methods that are currently disabled. This is a string * containing the IDs of all disabled input methods, each ID separated @@ -5388,6 +5435,8 @@ public final class Settings { */ public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; + private static final Validator SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Host name and port for global http proxy. Uses ':' seperator for * between host and port. @@ -5677,6 +5726,8 @@ public final class Settings { */ public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; + private static final Validator ACCESSIBILITY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Setting specifying if the accessibility shortcut is enabled. * @hide @@ -5684,6 +5735,8 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_ENABLED = "accessibility_shortcut_enabled"; + private static final Validator ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Setting specifying if the accessibility shortcut is enabled. * @hide @@ -5691,6 +5744,9 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN = "accessibility_shortcut_on_lock_screen"; + private static final Validator ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting specifying if the accessibility shortcut dialog has been shown to this user. * @hide @@ -5698,6 +5754,9 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN = "accessibility_shortcut_dialog_shown"; + private static final Validator ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting specifying the accessibility service to be toggled via the accessibility * shortcut. Must be its flattened {@link ComponentName}. @@ -5706,6 +5765,9 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service"; + private static final Validator ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR = + COMPONENT_NAME_VALIDATOR; + /** * Setting specifying the accessibility service or feature to be toggled via the * accessibility button in the navigation bar. This is either a flattened @@ -5716,17 +5778,32 @@ public final class Settings { public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT = "accessibility_button_target_component"; + private static final Validator ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR = + new Validator() { + @Override + public boolean validate(String value) { + // technically either ComponentName or class name, but there's proper value + // validation at callsites, so allow any non-null string + return value != null; + } + }; + /** * If touch exploration is enabled. */ public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled"; + private static final Validator TOUCH_EXPLORATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * List of the enabled accessibility providers. */ public static final String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services"; + private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * List of the accessibility services to which the user has granted * permission to put the device into touch exploration mode. @@ -5736,6 +5813,9 @@ public final class Settings { public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES = "touch_exploration_granted_accessibility_services"; + private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Uri of the slice that's presented on the keyguard. * Defaults to a slice with the date and next alarm. @@ -5754,6 +5834,8 @@ public final class Settings { @Deprecated public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; + private static final Validator ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether to draw text with high contrast while in accessibility mode. * @@ -5762,6 +5844,9 @@ public final class Settings { public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED = "high_text_contrast_enabled"; + private static final Validator ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies whether the display magnification is enabled via a system-wide * triple tap gesture. Display magnifications allows the user to zoom in the display content @@ -5774,6 +5859,9 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies whether the display magnification is enabled via a shortcut * affordance within the system's navigation area. Display magnifications allows the user to @@ -5785,6 +5873,9 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR + = BOOLEAN_VALIDATOR; + /** * Setting that specifies what the display magnification scale is. * Display magnifications allows the user to zoom in the display @@ -5798,6 +5889,9 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE = "accessibility_display_magnification_scale"; + private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR = + new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE); + /** * Unused mangnification setting * @@ -5850,6 +5944,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_ENABLED = "accessibility_captioning_enabled"; + private static final Validator ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies the language for captions as a locale string, * e.g. en_US. @@ -5860,6 +5957,8 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_LOCALE = "accessibility_captioning_locale"; + private static final Validator ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR = LOCALE_VALIDATOR; + /** * Integer property that specifies the preset style for captions, one * of: @@ -5874,6 +5973,10 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_PRESET = "accessibility_captioning_preset"; + private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2", + "3", "4"}); + /** * Integer property that specifes the background color for captions as a * packed 32-bit color. @@ -5884,6 +5987,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR = "accessibility_captioning_background_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * Integer property that specifes the foreground color for captions as a * packed 32-bit color. @@ -5894,6 +6000,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR = "accessibility_captioning_foreground_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * Integer property that specifes the edge type for captions, one of: * <ul> @@ -5908,6 +6017,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE = "accessibility_captioning_edge_type"; + private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"}); + /** * Integer property that specifes the edge color for captions as a * packed 32-bit color. @@ -5919,6 +6031,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR = "accessibility_captioning_edge_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * Integer property that specifes the window color for captions as a * packed 32-bit color. @@ -5929,6 +6044,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR = "accessibility_captioning_window_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * String property that specifies the typeface for captions, one of: * <ul> @@ -5944,6 +6062,10 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE = "accessibility_captioning_typeface"; + private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT", + "MONOSPACE", "SANS_SERIF", "SERIF"}); + /** * Floating point property that specifies font scaling for captions. * @@ -5952,12 +6074,18 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE = "accessibility_captioning_font_scale"; + private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR = + new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f); + /** * Setting that specifies whether display color inversion is enabled. */ public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies whether display color space adjustment is * enabled. @@ -5967,15 +6095,24 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED = "accessibility_display_daltonizer_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Integer property that specifies the type of color space adjustment to - * perform. Valid values are defined in AccessibilityManager. + * perform. Valid values are defined in AccessibilityManager: + * - AccessibilityManager.DALTONIZER_DISABLED = -1 + * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0 + * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12 * * @hide */ public static final String ACCESSIBILITY_DISPLAY_DALTONIZER = "accessibility_display_daltonizer"; + private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"}); + /** * Setting that specifies whether automatic click when the mouse pointer stops moving is * enabled. @@ -5985,6 +6122,9 @@ public final class Settings { public static final String ACCESSIBILITY_AUTOCLICK_ENABLED = "accessibility_autoclick_enabled"; + private static final Validator ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Integer setting specifying amount of time in ms the mouse pointer has to stay still * before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set. @@ -5995,6 +6135,9 @@ public final class Settings { public static final String ACCESSIBILITY_AUTOCLICK_DELAY = "accessibility_autoclick_delay"; + private static final Validator ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Whether or not larger size icons are used for the pointer of mouse/trackpad for * accessibility. @@ -6004,12 +6147,18 @@ public final class Settings { public static final String ACCESSIBILITY_LARGE_POINTER_ICON = "accessibility_large_pointer_icon"; + private static final Validator ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * The timeout for considering a press to be a long press in milliseconds. * @hide */ public static final String LONG_PRESS_TIMEOUT = "long_press_timeout"; + private static final Validator LONG_PRESS_TIMEOUT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * The duration in milliseconds between the first tap's up event and the second tap's * down event for an interaction to be considered part of the same multi-press. @@ -6063,16 +6212,22 @@ public final class Settings { */ public static final String TTS_DEFAULT_RATE = "tts_default_rate"; + private static final Validator TTS_DEFAULT_RATE_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Default text-to-speech engine pitch. 100 = 1x */ public static final String TTS_DEFAULT_PITCH = "tts_default_pitch"; + private static final Validator TTS_DEFAULT_PITCH_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Default text-to-speech engine. */ public static final String TTS_DEFAULT_SYNTH = "tts_default_synth"; + private static final Validator TTS_DEFAULT_SYNTH_VALIDATOR = PACKAGE_NAME_VALIDATOR; + /** * Default text-to-speech language. * @@ -6120,11 +6275,33 @@ public final class Settings { */ public static final String TTS_DEFAULT_LOCALE = "tts_default_locale"; + private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null || value.length() == 0) { + return false; + } + String[] ttsLocales = value.split(","); + boolean valid = true; + for (String ttsLocale : ttsLocales) { + String[] parts = ttsLocale.split(":"); + valid |= ((parts.length == 2) + && (parts[0].length() > 0) + && ANY_STRING_VALIDATOR.validate(parts[0]) + && LOCALE_VALIDATOR.validate(parts[1])); + } + return valid; + } + }; + /** * Space delimited list of plugin packages that are enabled. */ public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins"; + private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR = + new SettingsValidators.PackageNameListValidator(" "); + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON} * instead. @@ -6133,8 +6310,8 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON; - private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = - BOOLEAN_VALIDATOR; + private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = + BOOLEAN_VALIDATOR; /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} @@ -6144,6 +6321,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY; + private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT} * instead. @@ -6152,6 +6332,9 @@ public final class Settings { public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT; + private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} * instead. @@ -6310,6 +6493,9 @@ public final class Settings { public static final String PREFERRED_TTY_MODE = "preferred_tty_mode"; + private static final Validator PREFERRED_TTY_MODE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"}); + /** * Whether the enhanced voice privacy mode is enabled. * 0 = normal voice privacy @@ -6318,6 +6504,8 @@ public final class Settings { */ public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled"; + private static final Validator ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the TTY mode mode is enabled. * 0 = disabled @@ -6326,6 +6514,8 @@ public final class Settings { */ public static final String TTY_MODE_ENABLED = "tty_mode_enabled"; + private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Controls whether settings backup is enabled. * Type: int ( 0 = disabled, 1 = enabled ) @@ -6496,24 +6686,32 @@ public final class Settings { */ public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd"; + private static final Validator MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true) * @hide */ public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart"; + private static final Validator MOUNT_UMS_AUTOSTART_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true) * @hide */ public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt"; + private static final Validator MOUNT_UMS_PROMPT_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true) * @hide */ public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled"; + private static final Validator MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If nonzero, ANRs in invisible background processes bring up a dialog. * Otherwise, the process will be silently killed. @@ -6531,6 +6729,9 @@ public final class Settings { public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option"; + private static final Validator SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * The {@link ComponentName} string of the service to be used as the voice recognition * service. @@ -6556,6 +6757,8 @@ public final class Settings { */ public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker"; + private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR; + /** * The {@link ComponentName} string of the selected subtype of the selected spell checker * service which is one of the services managed by the text service manager. @@ -6565,13 +6768,18 @@ public final class Settings { public static final String SELECTED_SPELL_CHECKER_SUBTYPE = "selected_spell_checker_subtype"; + private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR = + COMPONENT_NAME_VALIDATOR; + /** - * The {@link ComponentName} string whether spell checker is enabled or not. + * Whether spell checker is enabled or not. * * @hide */ public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled"; + private static final Validator SPELL_CHECKER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * What happens when the user presses the Power button while in-call * and the screen is on.<br/> @@ -6583,6 +6791,9 @@ public final class Settings { */ public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior"; + private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"}); + /** * INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen". * @hide @@ -6638,12 +6849,16 @@ public final class Settings { */ public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled"; + private static final Validator WAKE_GESTURE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the device should doze if configured. * @hide */ public static final String DOZE_ENABLED = "doze_enabled"; + private static final Validator DOZE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether doze should be always on. * @hide @@ -6656,6 +6871,8 @@ public final class Settings { */ public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up"; + private static final Validator DOZE_PULSE_ON_PICK_UP_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the device should pulse on long press gesture. * @hide @@ -6668,6 +6885,8 @@ public final class Settings { */ public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap"; + private static final Validator DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per @@ -6682,6 +6901,8 @@ public final class Settings { */ public static final String SCREENSAVER_ENABLED = "screensaver_enabled"; + private static final Validator SCREENSAVER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The user's chosen screensaver components. * @@ -6691,6 +6912,9 @@ public final class Settings { */ public static final String SCREENSAVER_COMPONENTS = "screensaver_components"; + private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(","); + /** * If screensavers are enabled, whether the screensaver should be automatically launched * when the device is inserted into a (desk) dock. @@ -6698,6 +6922,8 @@ public final class Settings { */ public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock"; + private static final Validator SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If screensavers are enabled, whether the screensaver should be automatically launched * when the screen times out when not on battery. @@ -6705,6 +6931,8 @@ public final class Settings { */ public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep"; + private static final Validator SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If screensavers are enabled, the default screensaver component. * @hide @@ -6717,6 +6945,9 @@ public final class Settings { */ public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; + private static final Validator NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR = + COMPONENT_NAME_VALIDATOR; + /** * Whether NFC payment is handled by the foreground application or a default. * @hide @@ -6814,6 +7045,9 @@ public final class Settings { public static final String ENABLED_NOTIFICATION_ASSISTANT = "enabled_notification_assistant"; + private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Read only list of the service components that the current user has explicitly allowed to * see all of the user's notifications, separated by ':'. @@ -6825,6 +7059,9 @@ public final class Settings { @Deprecated public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; + private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Read only list of the packages that the current user has explicitly allowed to * manage do not disturb, separated by ':'. @@ -6837,6 +7074,9 @@ public final class Settings { public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; + private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR = + new SettingsValidators.PackageNameListValidator(":"); + /** * Defines whether managed profile ringtones should be synced from it's parent profile * <p> @@ -6850,6 +7090,8 @@ public final class Settings { @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; + private static final Validator SYNC_PARENT_SOUNDS_VALIDATOR = BOOLEAN_VALIDATOR; + /** @hide */ public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations"; @@ -6936,12 +7178,17 @@ public final class Settings { */ public static final String SLEEP_TIMEOUT = "sleep_timeout"; + private static final Validator SLEEP_TIMEOUT_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(-1, Integer.MAX_VALUE); + /** * Controls whether double tap to wake is enabled. * @hide */ public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake"; + private static final Validator DOUBLE_TAP_TO_WAKE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The current assistant component. It could be a voice interaction service, * or an activity that handles ACTION_ASSIST, or empty which means using the default @@ -6958,6 +7205,8 @@ public final class Settings { */ public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled"; + private static final Validator CAMERA_GESTURE_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the camera launch gesture to double tap the power button when the screen is off * should be disabled. @@ -6967,6 +7216,9 @@ public final class Settings { public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED = "camera_double_tap_power_gesture_disabled"; + private static final Validator CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether the camera double twist gesture to flip between front and back mode should be * enabled. @@ -6976,6 +7228,9 @@ public final class Settings { public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED = "camera_double_twist_to_flip_enabled"; + private static final Validator CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether or not the smart camera lift trigger that launches the camera when the user moves * the phone into a position for taking photos should be enabled. @@ -6998,6 +7253,9 @@ public final class Settings { */ public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled"; + private static final Validator ASSIST_GESTURE_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Sensitivity control for the assist gesture. * @@ -7005,6 +7263,9 @@ public final class Settings { */ public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity"; + private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR = + new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f); + /** * Whether the assist gesture should silence alerts. * @@ -7013,6 +7274,9 @@ public final class Settings { public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED = "assist_gesture_silence_alerts_enabled"; + private static final Validator ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether the assist gesture should wake the phone. * @@ -7021,6 +7285,9 @@ public final class Settings { public static final String ASSIST_GESTURE_WAKE_ENABLED = "assist_gesture_wake_enabled"; + private static final Validator ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether Assist Gesture Deferred Setup has been completed * @@ -7028,6 +7295,8 @@ public final class Settings { */ public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete"; + private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Control whether Night display is currently activated. * @hide @@ -7040,6 +7309,8 @@ public final class Settings { */ public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode"; + private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Control the color temperature of Night Display, represented in Kelvin. * @hide @@ -7047,6 +7318,9 @@ public final class Settings { public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE = "night_display_color_temperature"; + private static final Validator NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Custom time when Night display is scheduled to activate. * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). @@ -7055,6 +7329,9 @@ public final class Settings { public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time"; + private static final Validator NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Custom time when Night display is scheduled to deactivate. * Represented as milliseconds from midnight (e.g. 21600000 == 6am). @@ -7062,6 +7339,9 @@ public final class Settings { */ public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time"; + private static final Validator NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * A String representing the LocalDateTime when Night display was last activated. Use to * decide whether to apply the current activated state after a reboot or user change. In @@ -7079,6 +7359,9 @@ public final class Settings { */ public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; + private static final Validator ENABLED_VR_LISTENERS_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Behavior of the display while in VR mode. * @@ -7088,6 +7371,9 @@ public final class Settings { */ public static final String VR_DISPLAY_MODE = "vr_display_mode"; + private static final Validator VR_DISPLAY_MODE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"}); + /** * Lower the display persistence while the system is in VR mode. * @@ -7144,6 +7430,9 @@ public final class Settings { public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN = "automatic_storage_manager_days_to_retain"; + private static final Validator AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Default number of days of information for the automatic storage manager to retain. * @@ -7185,12 +7474,30 @@ public final class Settings { public static final String SYSTEM_NAVIGATION_KEYS_ENABLED = "system_navigation_keys_enabled"; + private static final Validator SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Holds comma separated list of ordering of QS tiles. * @hide */ public static final String QS_TILES = "sysui_qs_tiles"; + private static final Validator QS_TILES_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] tiles = value.split(","); + boolean valid = true; + for (String tile : tiles) { + // tile can be any non-empty string as specified by OEM + valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile)); + } + return valid; + } + }; + /** * Specifies whether the web action API is enabled. * @@ -7226,18 +7533,38 @@ public final class Settings { */ public static final String NOTIFICATION_BADGING = "notification_badging"; + private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Comma separated list of QS tiles that have been auto-added already. * @hide */ public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles"; + private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] tiles = value.split(","); + boolean valid = true; + for (String tile : tiles) { + // tile can be any non-empty string as specified by OEM + valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile)); + } + return valid; + } + }; + /** * Whether the Lockdown button should be shown in the power menu. * @hide */ public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu"; + private static final Validator LOCKDOWN_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Backup manager behavioral parameters. * This is encoded as a key=value list, separated by commas. Ex: @@ -7277,8 +7604,6 @@ public final class Settings { public static final String[] SETTINGS_TO_BACKUP = { BUGREPORT_IN_POWER_MENU, // moved to global ALLOW_MOCK_LOCATION, - PARENTAL_CONTROL_ENABLED, - PARENTAL_CONTROL_REDIRECT_URL, USB_MASS_STORAGE_ENABLED, // moved to global ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, ACCESSIBILITY_DISPLAY_DALTONIZER, @@ -7310,12 +7635,9 @@ public final class Settings { ACCESSIBILITY_CAPTIONING_TYPEFACE, ACCESSIBILITY_CAPTIONING_FONT_SCALE, ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, - TTS_USE_DEFAULTS, TTS_DEFAULT_RATE, TTS_DEFAULT_PITCH, TTS_DEFAULT_SYNTH, - TTS_DEFAULT_LANG, - TTS_DEFAULT_COUNTRY, TTS_ENABLED_PLUGINS, TTS_DEFAULT_LOCALE, SHOW_IME_WITH_HARD_KEYBOARD, @@ -7371,7 +7693,158 @@ public final class Settings { SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, }; - /** @hide */ + /** + * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ + public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); + static { + VALIDATORS.put(BUGREPORT_IN_POWER_MENU, BUGREPORT_IN_POWER_MENU_VALIDATOR); + VALIDATORS.put(ALLOW_MOCK_LOCATION, ALLOW_MOCK_LOCATION_VALIDATOR); + VALIDATORS.put(USB_MASS_STORAGE_ENABLED, USB_MASS_STORAGE_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, + ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER, + ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, + ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR); + VALIDATORS.put(AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR); + VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES, + ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR); + VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR); + VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR); + VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR); + VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_ENABLED, ACCESSIBILITY_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_BUTTON_TARGET_COMPONENT, + ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ENABLED, + ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, + ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SPEAK_PASSWORD, ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, + ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_PRESET, + ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_ENABLED, + ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_LOCALE, + ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR, + ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR, + ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_TYPE, + ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_COLOR, + ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_TYPEFACE, + ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FONT_SCALE, + ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, + ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_RATE, TTS_DEFAULT_RATE_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_PITCH, TTS_DEFAULT_PITCH_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_SYNTH, TTS_DEFAULT_SYNTH_VALIDATOR); + VALIDATORS.put(TTS_ENABLED_PLUGINS, TTS_ENABLED_PLUGINS_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_LOCALE, TTS_DEFAULT_LOCALE_VALIDATOR); + VALIDATORS.put(SHOW_IME_WITH_HARD_KEYBOARD, SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR); + VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR); + VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR); + VALIDATORS.put(WIFI_NUM_OPEN_NETWORKS_KEPT, WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR); + VALIDATORS.put(SELECTED_SPELL_CHECKER, SELECTED_SPELL_CHECKER_VALIDATOR); + VALIDATORS.put(SELECTED_SPELL_CHECKER_SUBTYPE, + SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR); + VALIDATORS.put(SPELL_CHECKER_ENABLED, SPELL_CHECKER_ENABLED_VALIDATOR); + VALIDATORS.put(MOUNT_PLAY_NOTIFICATION_SND, MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR); + VALIDATORS.put(MOUNT_UMS_AUTOSTART, MOUNT_UMS_AUTOSTART_VALIDATOR); + VALIDATORS.put(MOUNT_UMS_PROMPT, MOUNT_UMS_PROMPT_VALIDATOR); + VALIDATORS.put(MOUNT_UMS_NOTIFY_ENABLED, MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR); + VALIDATORS.put(SLEEP_TIMEOUT, SLEEP_TIMEOUT_VALIDATOR); + VALIDATORS.put(DOUBLE_TAP_TO_WAKE, DOUBLE_TAP_TO_WAKE_VALIDATOR); + VALIDATORS.put(WAKE_GESTURE_ENABLED, WAKE_GESTURE_ENABLED_VALIDATOR); + VALIDATORS.put(LONG_PRESS_TIMEOUT, LONG_PRESS_TIMEOUT_VALIDATOR); + VALIDATORS.put(CAMERA_GESTURE_DISABLED, CAMERA_GESTURE_DISABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_ENABLED, + ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_DELAY, ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_LARGE_POINTER_ICON, + ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR); + VALIDATORS.put(PREFERRED_TTY_MODE, PREFERRED_TTY_MODE_VALIDATOR); + VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED, + ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR); + VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR); + VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME, + NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE, + NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR); + VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR); + VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, + CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR); + VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, + CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR); + VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED, + SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR); + VALIDATORS.put(QS_TILES, QS_TILES_VALIDATOR); + VALIDATORS.put(DOZE_ENABLED, DOZE_ENABLED_VALIDATOR); + VALIDATORS.put(DOZE_PULSE_ON_PICK_UP, DOZE_PULSE_ON_PICK_UP_VALIDATOR); + VALIDATORS.put(DOZE_PULSE_ON_DOUBLE_TAP, DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR); + VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR); + VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, + AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, + ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR); + VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR); + VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR); + VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR); + VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR); + VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR); + VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_DOCK, SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR); + VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_SLEEP, SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR); + VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR); + VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, + SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR); + VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS, + ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting + VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT, + ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR); //legacy restore setting + VALIDATORS.put(ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, + ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting + } + + /** + * Keys we no longer back up under the current schema, but want to continue to + * process when restoring historical backup datasets. + * + * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ public static final String[] LEGACY_RESTORE_SETTINGS = { ENABLED_NOTIFICATION_LISTENERS, ENABLED_NOTIFICATION_ASSISTANT, @@ -8706,6 +9179,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay"; + private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * 802.11 country code in ISO 3166 format * @hide @@ -8735,6 +9211,9 @@ public final class Settings { */ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept"; + private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this. */ @@ -10780,7 +11259,15 @@ public final class Settings { LOCATION_GLOBAL_KILL_SWITCH, }; - /** @hide */ + /** + * Keys we no longer back up under the current schema, but want to continue to + * process when restoring historical backup datasets. + * + * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ public static final String[] LEGACY_RESTORE_SETTINGS = { }; diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java index 22ef3b6f346f..c384a2a6e0f5 100644 --- a/core/java/android/provider/SettingsValidators.java +++ b/core/java/android/provider/SettingsValidators.java @@ -21,6 +21,8 @@ import android.net.Uri; import com.android.internal.util.ArrayUtils; +import java.util.Locale; + /** * This class provides both interface for validation and common validators * used to ensure Settings have meaningful values. @@ -50,6 +52,18 @@ public class SettingsValidators { } }; + public static final Validator ANY_INTEGER_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + Integer.parseInt(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + }; + public static final Validator URI_VALIDATOR = new Validator() { @Override public boolean validate(String value) { @@ -80,6 +94,9 @@ public class SettingsValidators { // and underscores ('_'). However, individual package name parts may only // start with letters. // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package) + if (value == null) { + return false; + } String[] subparts = value.split("."); boolean isValidPackageName = true; for (String subpart : subparts) { @@ -106,10 +123,29 @@ public class SettingsValidators { @Override public boolean validate(String value) { + if (value == null) { + return false; + } return value.length() <= MAX_IPV6_LENGTH; } }; + public static final Validator LOCALE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + Locale[] validLocales = Locale.getAvailableLocales(); + for (Locale locale : validLocales) { + if (value.equals(locale.toString())) { + return true; + } + } + return false; + } + }; + public interface Validator { boolean validate(String value); } @@ -166,4 +202,48 @@ public class SettingsValidators { } } } + + public static final class ComponentNameListValidator implements Validator { + private final String mSeparator; + + public ComponentNameListValidator(String separator) { + mSeparator = separator; + } + + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] elements = value.split(mSeparator); + for (String element : elements) { + if (!COMPONENT_NAME_VALIDATOR.validate(element)) { + return false; + } + } + return true; + } + } + + public static final class PackageNameListValidator implements Validator { + private final String mSeparator; + + public PackageNameListValidator(String separator) { + mSeparator = separator; + } + + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] elements = value.split(mSeparator); + for (String element : elements) { + if (!PACKAGE_NAME_VALIDATOR.validate(element)) { + return false; + } + } + return true; + } + } } diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java index de05f50c9860..4f471a8a2f85 100644 --- a/core/java/android/text/style/BackgroundColorSpan.java +++ b/core/java/android/text/style/BackgroundColorSpan.java @@ -16,24 +16,48 @@ package android.text.style; +import android.annotation.ColorInt; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * Changes the background color of the text to which the span is attached. + * <p> + * For example, to set a green background color for a text you would create a {@link + * android.text.SpannableStringBuilder} based on the text and set the span. + * <pre>{@code + * SpannableString string = new SpannableString("Text with a background color span"); + *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * }</pre> + * <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" /> + * <figcaption>Set a background color for the text.</figcaption> + */ public class BackgroundColorSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { private final int mColor; - public BackgroundColorSpan(int color) { + /** + * Creates a {@link BackgroundColorSpan} from a color integer. + * <p> + * + * @param color color integer that defines the background color + * @see android.content.res.Resources#getColor(int, Resources.Theme) + */ + public BackgroundColorSpan(@ColorInt int color) { mColor = color; } - public BackgroundColorSpan(Parcel src) { + /** + * Creates a {@link BackgroundColorSpan} from a parcel. + */ + public BackgroundColorSpan(@NonNull Parcel src) { mColor = src.readInt(); } - + public int getSpanTypeId() { return getSpanTypeIdInternal(); } @@ -42,26 +66,40 @@ public class BackgroundColorSpan extends CharacterStyle public int getSpanTypeIdInternal() { return TextUtils.BACKGROUND_COLOR_SPAN; } - + public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + /** + * Flatten this object into a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); } + /** + * @return the background color of this span. + * @see BackgroundColorSpan#BackgroundColorSpan(int) + */ + @ColorInt public int getBackgroundColor() { return mColor; } + /** + * Updates the background color of the TextPaint. + */ @Override - public void updateDrawState(TextPaint ds) { - ds.bgColor = mColor; + public void updateDrawState(@NonNull TextPaint textPaint) { + textPaint.bgColor = mColor; } } diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java index 2bc6d5406ade..08ab2a1f1a20 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -17,24 +17,48 @@ package android.text.style; import android.annotation.ColorInt; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * Changes the color of the text to which the span is attached. + * <p> + * For example, to set a green text color you would create a {@link + * android.text.SpannableStringBuilder} based on the text and set the span. + * <pre>{@code + * SpannableString string = new SpannableString("Text with a foreground color span"); + *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * }</pre> + * <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" /> + * <figcaption>Set a text color.</figcaption> + */ public class ForegroundColorSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { private final int mColor; + /** + * Creates a {@link ForegroundColorSpan} from a color integer. + * <p> + * To get the color integer associated with a particular color resource ID, use + * {@link android.content.res.Resources#getColor(int, Resources.Theme)} + * + * @param color color integer that defines the text color + */ public ForegroundColorSpan(@ColorInt int color) { mColor = color; } - public ForegroundColorSpan(Parcel src) { + /** + * Creates a {@link ForegroundColorSpan} from a parcel. + */ + public ForegroundColorSpan(@NonNull Parcel src) { mColor = src.readInt(); } - + public int getSpanTypeId() { return getSpanTypeIdInternal(); } @@ -48,22 +72,35 @@ public class ForegroundColorSpan extends CharacterStyle return 0; } - public void writeToParcel(Parcel dest, int flags) { + /** + * Flatten this object into a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); } + /** + * @return the foreground color of this span. + * @see ForegroundColorSpan#ForegroundColorSpan(int) + */ @ColorInt public int getForegroundColor() { return mColor; } + /** + * Updates the color of the TextPaint to the foreground color. + */ @Override - public void updateDrawState(TextPaint ds) { - ds.setColor(mColor); + public void updateDrawState(@NonNull TextPaint textPaint) { + textPaint.setColor(mColor); } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index ce8998fcb863..5a09dab552e3 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -16,6 +16,7 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512; @@ -42,6 +43,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -105,7 +107,8 @@ public class ApkSignatureSchemeV2Verifier { */ public static X509Certificate[][] verify(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { - return verify(apkFile, true); + VerifiedSigner vSigner = verify(apkFile, true); + return vSigner.certs; } /** @@ -119,10 +122,11 @@ public class ApkSignatureSchemeV2Verifier { */ public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { - return verify(apkFile, false); + VerifiedSigner vSigner = verify(apkFile, false); + return vSigner.certs; } - private static X509Certificate[][] verify(String apkFile, boolean verifyIntegrity) + private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { return verify(apk, verifyIntegrity); @@ -138,7 +142,7 @@ public class ApkSignatureSchemeV2Verifier { * verify. * @throws IOException if an I/O error occurs while reading the APK file. */ - private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity) + private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { SignatureInfo signatureInfo = findSignature(apk); return verify(apk, signatureInfo, verifyIntegrity); @@ -163,7 +167,7 @@ public class ApkSignatureSchemeV2Verifier { * @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it * against the APK file. */ - private static X509Certificate[][] verify( + private static VerifiedSigner verify( RandomAccessFile apk, SignatureInfo signatureInfo, boolean doVerifyIntegrity) throws SecurityException, IOException { @@ -207,7 +211,14 @@ public class ApkSignatureSchemeV2Verifier { ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } - return signerCerts.toArray(new X509Certificate[signerCerts.size()][]); + byte[] verityRootHash = null; + if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { + verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + } + + return new VerifiedSigner( + signerCerts.toArray(new X509Certificate[signerCerts.size()][]), + verityRootHash); } private static X509Certificate[] verifySigner( @@ -383,6 +394,24 @@ public class ApkSignatureSchemeV2Verifier { return; } + static byte[] getVerityRootHash(String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + VerifiedSigner vSigner = verify(apk, false); + return vSigner.verityRootHash; + } + } + + static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo); + } + } + private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: @@ -400,4 +429,20 @@ public class ApkSignatureSchemeV2Verifier { return false; } } + + /** + * Verified APK Signature Scheme v2 signer. + * + * @hide for internal use only. + */ + public static class VerifiedSigner { + public final X509Certificate[][] certs; + public final byte[] verityRootHash; + + public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) { + this.certs = certs; + this.verityRootHash = verityRootHash; + } + + } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index c9e67fe1c9ef..1b04eb2f7d44 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -16,6 +16,7 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512; @@ -43,6 +44,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -211,6 +213,10 @@ public class ApkSignatureSchemeV3Verifier { ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } + if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { + result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + } + return result; } @@ -499,6 +505,24 @@ public class ApkSignatureSchemeV3Verifier { return new VerifiedProofOfRotation(certs, flagsList); } + static byte[] getVerityRootHash(String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + VerifiedSigner vSigner = verify(apk, false); + return vSigner.verityRootHash; + } + } + + static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo); + } + } + private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: @@ -541,6 +565,8 @@ public class ApkSignatureSchemeV3Verifier { public final X509Certificate[] certs; public final VerifiedProofOfRotation por; + public byte[] verityRootHash; + public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) { this.certs = certs; this.por = por; diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index 555c4740389a..a2a76169c83a 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -36,7 +36,9 @@ import libcore.io.IoUtils; import java.io.IOException; import java.io.InputStream; +import java.security.DigestException; import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.ArrayList; @@ -367,4 +369,51 @@ public class ApkSignatureVerifier { // v2 didn't work, try jarsigner return verifyV1Signature(apkPath, false); } + + /** + * @return the verity root hash in the Signing Block. + */ + public static byte[] getVerityRootHash(String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + // first try v3 + try { + return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath); + } catch (SignatureNotFoundException e) { + // try older version + } + return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath); + } + + /** + * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code + * ByteBufferFactory}. + * + * @return the verity root hash of the generated Merkle tree. + */ + public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + // first try v3 + try { + return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory); + } catch (SignatureNotFoundException e) { + // try older version + } + return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory); + } + + /** + * Result of a successful APK verification operation. + */ + public static class Result { + public final Certificate[][] certs; + public final Signature[] sigs; + public final int signatureSchemeVersion; + + public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) { + this.certs = certs; + this.sigs = sigs; + this.signatureSchemeVersion = signingVersion; + } + } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 9d53847f8110..4146f6fab011 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -306,6 +306,26 @@ final class ApkSigningBlockUtils { } /** + * Generates the fsverity header and hash tree to be used by kernel for the given apk. This + * method does not check whether the root hash exists in the Signing Block or not. + * + * <p>The output is stored in the {@link ByteBuffer} created by the given {@link + * ByteBufferFactory}. + * + * @return the root hash of the generated hash tree. + */ + public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory, + SignatureInfo signatureInfo) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk, + signatureInfo, bufferFactory); + return result.rootHash; + } + } + + /** * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. * * @throws IOException if an I/O error occurs while reading the file. diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 7412ef411fb4..a0d5e4c2dd8e 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -164,11 +164,11 @@ abstract class ApkVerityBuilder { } private void fillUpLastOutputChunk() { - int extra = (int) (BUFFER_SIZE - mOutput.position() % BUFFER_SIZE); - if (extra == 0) { + int lastBlockSize = (int) (mOutput.position() % BUFFER_SIZE); + if (lastBlockSize == 0) { return; } - mOutput.put(ByteBuffer.allocate(extra)); + mOutput.put(ByteBuffer.allocate(BUFFER_SIZE - lastBlockSize)); } } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 388180dc61e0..e2d1ad59043e 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -23,6 +23,7 @@ import android.net.wifi.WifiActivityEnergyInfo; import android.os.ParcelFileDescriptor; import android.os.WorkSource; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.health.HealthStatsParceler; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; @@ -91,6 +92,7 @@ interface IBatteryStats { void noteVibratorOff(int uid); void noteStartGps(int uid); void noteStopGps(int uid); + void noteGpsSignalQuality(int signalLevel); void noteScreenState(int state); void noteScreenBrightness(int brightness); void noteUserActivity(int uid, int event); @@ -140,6 +142,9 @@ interface IBatteryStats { /** {@hide} */ CellularBatteryStats getCellularBatteryStats(); + /** {@hide} */ + GpsBatteryStats getGpsBatteryStats(); + HealthStatsParceler takeUidSnapshot(int uid); HealthStatsParceler[] takeUidSnapshots(in int[] uid); diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 15dc6f507093..5a59e70865e5 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -665,14 +665,14 @@ public class BatteryStatsHelper { /** * Calculate the baseline power usage for the device when it is in suspend and idle. - * The device is drawing POWER_CPU_IDLE power at its lowest power state. - * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held. + * The device is drawing POWER_CPU_SUSPEND power at its lowest power state. + * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held. */ private void addIdleUsage() { final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) * - mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); + mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND); final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) * - mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); + mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); if (DEBUG && totalPowerMah != 0) { Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index ac5dbc4e175a..799e3e8d38d0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -34,6 +34,7 @@ import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Build; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.FileUtils; import android.os.Handler; import android.os.IBatteryPropertiesRegistrar; @@ -78,6 +79,7 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; @@ -153,11 +155,11 @@ public class BatteryStatsImpl extends BatteryStats { MAX_HISTORY_BUFFER = 96*1024; // 96KB MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB } else { - MAX_HISTORY_ITEMS = 2000; - MAX_MAX_HISTORY_ITEMS = 3000; - MAX_WAKELOCKS_PER_UID = 100; - MAX_HISTORY_BUFFER = 256*1024; // 256KB - MAX_MAX_HISTORY_BUFFER = 320*1024; // 256KB + MAX_HISTORY_ITEMS = 4000; + MAX_MAX_HISTORY_ITEMS = 6000; + MAX_WAKELOCKS_PER_UID = 200; + MAX_HISTORY_BUFFER = 512*1024; // 512KB + MAX_MAX_HISTORY_BUFFER = 640*1024; // 640KB } } @@ -198,6 +200,12 @@ public class BatteryStatsImpl extends BatteryStats { protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader(); @VisibleForTesting + protected KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader = + new KernelUidCpuActiveTimeReader(); + @VisibleForTesting + protected KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader = + new KernelUidCpuClusterTimeReader(); + @VisibleForTesting protected KernelSingleUidTimeReader mKernelSingleUidTimeReader; private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats @@ -666,6 +674,10 @@ public class BatteryStatsImpl extends BatteryStats { int mCameraOnNesting; StopwatchTimer mCameraOnTimer; + int mGpsSignalQualityBin = -1; + final StopwatchTimer[] mGpsSignalQualityTimer = + new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; + int mPhoneSignalStrengthBin = -1; int mPhoneSignalStrengthBinRaw = -1; final StopwatchTimer[] mPhoneSignalStrengthsTimer = @@ -3880,6 +3892,8 @@ public class BatteryStatsImpl extends BatteryStats { } mKernelUidCpuTimeReader.removeUid(isolatedUid); mKernelUidCpuFreqTimeReader.removeUid(isolatedUid); + mKernelUidCpuActiveTimeReader.removeUid(isolatedUid); + mKernelUidCpuClusterTimeReader.removeUid(isolatedUid); } public int mapUid(int uid) { @@ -4575,10 +4589,37 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + stopAllGpsSignalQualityTimersLocked(-1); + mGpsSignalQualityBin = -1; } getUidStatsLocked(uid).noteStopGps(elapsedRealtime); } + public void noteGpsSignalQualityLocked(int signalLevel) { + if (mGpsNesting == 0) { + return; + } + if (signalLevel < 0 || signalLevel >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) { + stopAllGpsSignalQualityTimersLocked(-1); + return; + } + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); + if (mGpsSignalQualityBin != signalLevel) { + if (mGpsSignalQualityBin >= 0) { + mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtime); + } + if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) { + mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtime); + } + mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK) + | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT); + addHistoryRecordLocked(elapsedRealtime, uptime); + mGpsSignalQualityBin = signalLevel; + } + return; + } + public void noteScreenStateLocked(int state) { state = mPretendScreenOff ? Display.STATE_OFF : state; @@ -4912,6 +4953,18 @@ public class BatteryStatsImpl extends BatteryStats { mDailyPackageChanges.add(pc); } + void stopAllGpsSignalQualityTimersLocked(int except) { + final long elapsedRealtime = mClocks.elapsedRealtime(); + for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + if (i == except) { + continue; + } + while (mGpsSignalQualityTimer[i].isRunningLocked()) { + mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtime); + } + } + } + public void notePhoneOnLocked() { if (!mPhoneOn) { final long elapsedRealtime = mClocks.elapsedRealtime(); @@ -6123,6 +6176,20 @@ public class BatteryStatsImpl extends BatteryStats { return val; } + @Override public long getGpsSignalQualityTime(int strengthBin, + long elapsedRealtimeUs, int which) { + if (strengthBin < 0 || strengthBin >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) { + return 0; + } + return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked( + elapsedRealtimeUs, which); + } + + @Override public long getGpsBatteryDrainMaMs() { + //TODO: Add GPS power computation (b/67213967) + return 0; + } + @Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) { return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -6479,9 +6546,11 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mUserCpuTime; LongSamplingCounter mSystemCpuTime; LongSamplingCounter[][] mCpuClusterSpeedTimesUs; + LongSamplingCounter mCpuActiveTimeMs; LongSamplingCounterArray mCpuFreqTimeMs; LongSamplingCounterArray mScreenOffCpuFreqTimeMs; + LongSamplingCounterArray mCpuClusterTimesMs; LongSamplingCounterArray[] mProcStateTimeMs; LongSamplingCounterArray[] mProcStateScreenOffTimeMs; @@ -6551,6 +6620,8 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); + mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); + mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase); mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) { @Override public Wakelock instantiateObject() { @@ -6598,6 +6669,17 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public long getCpuActiveTime() { + return mCpuActiveTimeMs.getCountLocked(STATS_SINCE_CHARGED); + } + + @Override + public long[] getCpuClusterTimes() { + return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED); + } + + + @Override public long[] getCpuFreqTimes(int which, int procState) { if (which < 0 || which >= NUM_PROCESS_STATE) { return null; @@ -7660,6 +7742,9 @@ public class BatteryStatsImpl extends BatteryStats { mScreenOffCpuFreqTimeMs.reset(false); } + mCpuActiveTimeMs.reset(false); + mCpuClusterTimesMs.reset(false); + if (mProcStateTimeMs != null) { for (LongSamplingCounterArray counters : mProcStateTimeMs) { if (counters != null) { @@ -7864,6 +7949,8 @@ public class BatteryStatsImpl extends BatteryStats { if (mScreenOffCpuFreqTimeMs != null) { mScreenOffCpuFreqTimeMs.detach(); } + mCpuActiveTimeMs.detach(); + mCpuClusterTimesMs.detach(); if (mProcStateTimeMs != null) { for (LongSamplingCounterArray counters : mProcStateTimeMs) { @@ -8139,6 +8226,10 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs); LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs); + + mCpuActiveTimeMs.writeToParcel(out); + mCpuClusterTimesMs.writeToParcel(out); + if (mProcStateTimeMs != null) { out.writeInt(mProcStateTimeMs.length); for (LongSamplingCounterArray counters : mProcStateTimeMs) { @@ -8456,6 +8547,9 @@ public class BatteryStatsImpl extends BatteryStats { mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel( in, mBsi.mOnBatteryScreenOffTimeBase); + mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); + mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in); + int length = in.readInt(); if (length == NUM_PROCESS_STATE) { mProcStateTimeMs = new LongSamplingCounterArray[length]; @@ -9822,6 +9916,10 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null, mOnBatteryTimeBase); } + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null, + mOnBatteryTimeBase); + } mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase); mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase); mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase); @@ -10511,6 +10609,9 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i].reset(false); } mWifiMulticastWakelockTimer.reset(false); + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].reset(false); + } mWifiActivity.reset(false); mBluetoothActivity.reset(false); mModemActivity.reset(false); @@ -11437,6 +11538,8 @@ public class BatteryStatsImpl extends BatteryStats { if (!mOnBatteryInternal) { mKernelUidCpuTimeReader.readDelta(null); mKernelUidCpuFreqTimeReader.readDelta(null); + mKernelUidCpuActiveTimeReader.readDelta(null); + mKernelUidCpuClusterTimeReader.readDelta(null); for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { mKernelCpuSpeedReaders[cluster].readDelta(); } @@ -11453,6 +11556,8 @@ public class BatteryStatsImpl extends BatteryStats { updateClusterSpeedTimes(updatedUids); } readKernelUidCpuFreqTimesLocked(partialTimersToConsider); + readKernelUidCpuActiveTimesLocked(); + readKernelUidCpuClusterTimesLocked(); } /** @@ -11764,6 +11869,64 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Take a snapshot of the cpu active times spent by each uid and update the corresponding + * counters. + */ + @VisibleForTesting + public void readKernelUidCpuActiveTimesLocked() { + final long startTimeMs = mClocks.uptimeMillis(); + mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> { + uid = mapUid(uid); + if (Process.isIsolated(uid)) { + mKernelUidCpuActiveTimeReader.removeUid(uid); + Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid); + return; + } + if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { + Slog.w(TAG, "Got active times for an invalid user's uid " + uid); + mKernelUidCpuActiveTimeReader.removeUid(uid); + return; + } + final Uid u = getUidStatsLocked(uid); + u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs); + }); + + final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; + if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) { + Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms"); + } + } + + /** + * Take a snapshot of the cpu cluster times spent by each uid and update the corresponding + * counters. + */ + @VisibleForTesting + public void readKernelUidCpuClusterTimesLocked() { + final long startTimeMs = mClocks.uptimeMillis(); + mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> { + uid = mapUid(uid); + if (Process.isIsolated(uid)) { + mKernelUidCpuClusterTimeReader.removeUid(uid); + Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid); + return; + } + if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { + Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid); + mKernelUidCpuClusterTimeReader.removeUid(uid); + return; + } + final Uid u = getUidStatsLocked(uid); + u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs); + }); + + final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; + if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) { + Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms"); + } + } + boolean setChargingLocked(boolean charging) { if (mCharging != charging) { mCharging = charging; @@ -12367,6 +12530,21 @@ public class BatteryStatsImpl extends BatteryStats { return s; } + /*@hide */ + public GpsBatteryStats getGpsBatteryStats() { + GpsBatteryStats s = new GpsBatteryStats(); + final int which = STATS_SINCE_CHARGED; + final long rawRealTime = SystemClock.elapsedRealtime() * 1000; + s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); + s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs()); + long[] time = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; + for (int i=0; i<time.length; i++) { + time[i] = getGpsSignalQualityTime(i, rawRealTime, which) / 1000; + } + s.setTimeInGpsSignalQualityLevel(time); + return s; + } + @Override public LevelStepTracker getChargeLevelStepTracker() { return mChargeStepTracker; @@ -13044,6 +13222,9 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in); } + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in); + } mWifiActivity.readSummaryFromParcel(in); mBluetoothActivity.readSummaryFromParcel(in); mModemActivity.readSummaryFromParcel(in); @@ -13249,6 +13430,10 @@ public class BatteryStatsImpl extends BatteryStats { in, mOnBatteryTimeBase); u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked( in, mOnBatteryScreenOffTimeBase); + + u.mCpuActiveTimeMs.readSummaryFromParcelLocked(in); + u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in); + int length = in.readInt(); if (length == Uid.NUM_PROCESS_STATE) { u.mProcStateTimeMs = new LongSamplingCounterArray[length]; @@ -13482,6 +13667,9 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); } + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); + } mWifiActivity.writeSummaryToParcel(out); mBluetoothActivity.writeSummaryToParcel(out); mModemActivity.writeSummaryToParcel(out); @@ -13725,6 +13913,9 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs); LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs); + u.mCpuActiveTimeMs.writeSummaryFromParcelLocked(out); + u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out); + if (u.mProcStateTimeMs != null) { out.writeInt(u.mProcStateTimeMs.length); for (LongSamplingCounterArray counters : u.mProcStateTimeMs) { @@ -13954,7 +14145,10 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null, mOnBatteryTimeBase, in); } - + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, + null, mOnBatteryTimeBase, in); + } mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS, in); mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, @@ -14154,6 +14348,9 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime); } + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime); + } mWifiActivity.writeToParcel(out, 0); mBluetoothActivity.writeToParcel(out, 0); mModemActivity.writeToParcel(out, 0); @@ -14348,6 +14545,10 @@ public class BatteryStatsImpl extends BatteryStats { pr.println("*** Wifi signal strength #" + i + ":"); mWifiSignalStrengthsTimer[i].logState(pr, " "); } + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + pr.println("*** GPS signal quality #" + i + ":"); + mGpsSignalQualityTimer[i].logState(pr, " "); + } pr.println("*** Flashlight timer:"); mFlashlightOnTimer.logState(pr, " "); pr.println("*** Camera timer:"); diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index bb743c157d1f..a34e7f50c9c9 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -31,8 +31,7 @@ public class CpuPowerCalculator extends PowerCalculator { @Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - + long rawUptimeUs, int statsType) { app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; final int numClusters = mProfile.getNumCpuClusters(); @@ -42,7 +41,7 @@ public class CpuPowerCalculator extends PowerCalculator { for (int speed = 0; speed < speedsForCluster; speed++) { final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); final double cpuSpeedStepPower = timeUs * - mProfile.getAveragePowerForCpu(cluster, speed); + mProfile.getAveragePowerForCpuCore(cluster, speed); if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + speed + " timeUs=" + timeUs + " power=" @@ -51,6 +50,25 @@ public class CpuPowerCalculator extends PowerCalculator { cpuPowerMaUs += cpuSpeedStepPower; } } + cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower( + PowerProfile.POWER_CPU_ACTIVE); + long[] cpuClusterTimes = u.getCpuClusterTimes(); + if (cpuClusterTimes != null) { + if (cpuClusterTimes.length == numClusters) { + for (int i = 0; i < numClusters; i++) { + double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i); + cpuPowerMaUs += power; + if (DEBUG) { + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs=" + + cpuClusterTimes[i] + " power=" + + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR)); + } + } + } else { + Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # " + + numClusters + " actual # " + cpuClusterTimes.length); + } + } app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) { diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java new file mode 100644 index 000000000000..cb96c5cdfb60 --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java @@ -0,0 +1,146 @@ +/* + * 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.internal.os; + +import android.annotation.Nullable; +import android.os.StrictMode; +import android.os.SystemClock; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * Reads /proc/uid_concurrent_active_time which has the format: + * active: X (X is # cores) + * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) + * [uid1]: [time-0] [time-1] [time-2] ... ... + * ... + * Time-N means the CPU time a UID spent running concurrently with N other processes. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a + * proper delta. + */ +public class KernelUidCpuActiveTimeReader { + private static final boolean DEBUG = false; + private static final String TAG = "KernelUidCpuActiveTimeReader"; + private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time"; + + private int mCoreCount; + private long mLastTimeReadMs; + private long mNowTimeMs; + private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>(); + + public interface Callback { + void onUidCpuActiveTime(int uid, long cpuActiveTimeMs); + } + + public void readDelta(@Nullable Callback cb) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { + mNowTimeMs = SystemClock.elapsedRealtime(); + readDeltaInternal(reader, cb); + mLastTimeReadMs = mNowTimeMs; + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } + + public void removeUid(int uid) { + mLastUidCpuActiveTimeMs.delete(uid); + } + + public void removeUidsInRange(int startUid, int endUid) { + if (endUid < startUid) { + Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); + return; + } + mLastUidCpuActiveTimeMs.put(startUid, null); + mLastUidCpuActiveTimeMs.put(endUid, null); + final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); + final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); + mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); + } + + @VisibleForTesting + public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException { + String line = reader.readLine(); + if (line == null || !line.startsWith("active:")) { + Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); + return; + } + if (mCoreCount == 0) { + mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1)); + } + while ((line = reader.readLine()) != null) { + final int index = line.indexOf(' '); + final int uid = Integer.parseInt(line.substring(0, index - 1), 10); + readTimesForUid(uid, line.substring(index + 1), cb); + } + } + + private void readTimesForUid(int uid, String line, @Nullable Callback cb) { + long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid); + if (lastActiveTime == null) { + lastActiveTime = new long[mCoreCount]; + mLastUidCpuActiveTimeMs.put(uid, lastActiveTime); + } + final String[] timesStr = line.split(" "); + if (timesStr.length != mCoreCount) { + Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d", + timesStr.length, mCoreCount)); + return; + } + long sumDeltas = 0; + final long[] curActiveTime = new long[mCoreCount]; + boolean notify = false; + for (int i = 0; i < mCoreCount; i++) { + // Times read will be in units of 10ms + curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10; + long delta = curActiveTime[i] - lastActiveTime[i]; + if (delta < 0 || curActiveTime[i] < 0) { + if (DEBUG) { + final StringBuilder sb = new StringBuilder(); + sb.append(String.format("Malformed cpu active time for UID=%d\n", uid)); + sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i])); + sb.append("times=("); + TimeUtils.formatDuration(mLastTimeReadMs, sb); + sb.append(","); + TimeUtils.formatDuration(mNowTimeMs, sb); + sb.append(")"); + Slog.e(TAG, sb.toString()); + } + return; + } + notify |= delta > 0; + sumDeltas += delta / (i + 1); + } + if (notify) { + System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount); + if (cb != null) { + cb.onUidCpuActiveTime(uid, sumDeltas); + } + } + } +} diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java new file mode 100644 index 000000000000..85153bc45d07 --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java @@ -0,0 +1,178 @@ +/* + * 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.internal.os; + +import android.annotation.Nullable; +import android.os.StrictMode; +import android.os.SystemClock; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Reads /proc/uid_concurrent_policy_time which has the format: + * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4) + * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * ... + * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a proper + * delta. + */ +public class KernelUidCpuClusterTimeReader { + + private static final boolean DEBUG = false; + private static final String TAG = "KernelUidCpuClusterTimeReader"; + private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time"; + + // mCoreOnCluster[i] is the # of cores on cluster i + private int[] mCoreOnCluster; + private int mCores; + private long mLastTimeReadMs; + private long mNowTimeMs; + private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>(); + + public interface Callback { + /** + * @param uid + * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of + * processes running concurrently with this uid. + */ + void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs); + } + + public void readDelta(@Nullable Callback cb) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { + mNowTimeMs = SystemClock.elapsedRealtime(); + readDeltaInternal(reader, cb); + mLastTimeReadMs = mNowTimeMs; + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } + + public void removeUid(int uid) { + mLastUidPolicyTimeMs.delete(uid); + } + + public void removeUidsInRange(int startUid, int endUid) { + if (endUid < startUid) { + Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); + return; + } + mLastUidPolicyTimeMs.put(startUid, null); + mLastUidPolicyTimeMs.put(endUid, null); + final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid); + final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid); + mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); + } + + @VisibleForTesting + public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException { + String line = reader.readLine(); + if (line == null || !line.startsWith("policy")) { + Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); + return; + } + if (mCoreOnCluster == null) { + List<Integer> list = new ArrayList<>(); + String[] policies = line.split(" "); + + if (policies.length == 0 || policies.length % 2 != 0) { + Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); + return; + } + + for (int i = 0; i < policies.length; i+=2) { + list.add(Integer.parseInt(policies[i+1])); + } + + mCoreOnCluster = new int[list.size()]; + for(int i=0;i<list.size();i++){ + mCoreOnCluster[i] = list.get(i); + mCores += mCoreOnCluster[i]; + } + } + while ((line = reader.readLine()) != null) { + final int index = line.indexOf(' '); + final int uid = Integer.parseInt(line.substring(0, index - 1), 10); + readTimesForUid(uid, line.substring(index + 1), cb); + } + } + + private void readTimesForUid(int uid, String line, @Nullable Callback cb) { + long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid); + if (lastPolicyTime == null) { + lastPolicyTime = new long[mCores]; + mLastUidPolicyTimeMs.put(uid, lastPolicyTime); + } + final String[] timeStr = line.split(" "); + if (timeStr.length != mCores) { + Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d", + timeStr.length, mCores)); + return; + } + final long[] deltaPolicyTime = new long[mCores]; + final long[] currPolicyTime = new long[mCores]; + boolean notify = false; + for (int i = 0; i < mCores; i++) { + // Times read will be in units of 10ms + currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10; + deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i]; + if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) { + if (DEBUG) { + final StringBuilder sb = new StringBuilder(); + sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid)); + sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i])); + sb.append("times=("); + TimeUtils.formatDuration(mLastTimeReadMs, sb); + sb.append(","); + TimeUtils.formatDuration(mNowTimeMs, sb); + sb.append(")"); + Slog.e(TAG, sb.toString()); + } + return; + } + notify |= deltaPolicyTime[i] > 0; + } + if (notify) { + System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores); + if (cb != null) { + final long[] times = new long[mCoreOnCluster.length]; + int core = 0; + for (int i = 0; i < mCoreOnCluster.length; i++) { + for (int j = 0; j < mCoreOnCluster[i]; j++) { + times[i] += deltaPolicyTime[core++] / (j+1); + } + } + cb.onUidCpuPolicyTime(uid, times); + } + } + } +} diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 872b465a9ca5..fcbbcd036740 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -43,23 +44,25 @@ public class PowerProfile { public static final String POWER_NONE = "none"; /** - * Power consumption when CPU is in power collapse mode. + * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode. + * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should + * be zero on devices that can go into full CPU power collapse even when a wake + * lock is held. Otherwise, this is the power consumption in addition to + * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity. + * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters + * and cores. + * + * CPU Power Equation (assume two clusters): + * Total power = POWER_CPU_SUSPEND (always added) + * + POWER_CPU_IDLE (skip this and below if in power collapse mode) + * + POWER_CPU_ACTIVE (skip this and below if CPU is not running, but a wakelock + * is held) + * + cluster_power.cluster0 + cluster_power.cluster1 (skip cluster not running) + * + core_power.cluster0 * num running cores in cluster 0 + * + core_power.cluster1 * num running cores in cluster 1 */ + public static final String POWER_CPU_SUSPEND = "cpu.suspend"; public static final String POWER_CPU_IDLE = "cpu.idle"; - - /** - * Power consumption when CPU is awake (when a wake lock is held). This - * should be 0 on devices that can go into full CPU power collapse even - * when a wake lock is held. Otherwise, this is the power consumption in - * addition to POWER_CPU_IDLE due to a wake lock being held but with no - * CPU activity. - */ - public static final String POWER_CPU_AWAKE = "cpu.awake"; - - /** - * Power consumption when CPU is in power collapse mode. - */ - @Deprecated public static final String POWER_CPU_ACTIVE = "cpu.active"; /** @@ -182,9 +185,6 @@ public class PowerProfile { */ public static final String POWER_CAMERA = "camera.avg"; - @Deprecated - public static final String POWER_CPU_SPEEDS = "cpu.speeds"; - /** * Power consumed by wif batched scaning. Broken down into bins by * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels @@ -197,7 +197,15 @@ public class PowerProfile { */ public static final String POWER_BATTERY_CAPACITY = "battery.capacity"; - static final HashMap<String, Object> sPowerMap = new HashMap<>(); + /** + * A map from Power Use Item to its power consumption. + */ + static final HashMap<String, Double> sPowerItemMap = new HashMap<>(); + /** + * A map from Power Use Item to an array of its power consumption + * (for items with variable power e.g. CPU). + */ + static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>(); private static final String TAG_DEVICE = "device"; private static final String TAG_ITEM = "item"; @@ -207,23 +215,32 @@ public class PowerProfile { private static final Object sLock = new Object(); + @VisibleForTesting public PowerProfile(Context context) { - // Read the XML file for the given profile (normally only one per - // device) + this(context, false); + } + + /** + * For PowerProfileTest + */ + @VisibleForTesting + public PowerProfile(Context context, boolean forTest) { + // Read the XML file for the given profile (normally only one per device) synchronized (sLock) { - if (sPowerMap.size() == 0) { - readPowerValuesFromXml(context); + if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { + readPowerValuesFromXml(context, forTest); } initCpuClusters(); } } - private void readPowerValuesFromXml(Context context) { - int id = com.android.internal.R.xml.power_profile; + private void readPowerValuesFromXml(Context context, boolean forTest) { + final int id = forTest ? com.android.internal.R.xml.power_profile_test : + com.android.internal.R.xml.power_profile; final Resources resources = context.getResources(); XmlResourceParser parser = resources.getXml(id); boolean parsingArray = false; - ArrayList<Double> array = new ArrayList<Double>(); + ArrayList<Double> array = new ArrayList<>(); String arrayName = null; try { @@ -237,7 +254,7 @@ public class PowerProfile { if (parsingArray && !element.equals(TAG_ARRAYITEM)) { // Finish array - sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()])); parsingArray = false; } if (element.equals(TAG_ARRAY)) { @@ -255,7 +272,7 @@ public class PowerProfile { } catch (NumberFormatException nfe) { } if (element.equals(TAG_ITEM)) { - sPowerMap.put(name, value); + sPowerItemMap.put(name, value); } else if (parsingArray) { array.add(value); } @@ -263,7 +280,7 @@ public class PowerProfile { } } if (parsingArray) { - sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()])); } } catch (XmlPullParserException e) { throw new RuntimeException(e); @@ -300,52 +317,56 @@ public class PowerProfile { String key = configResIdKeys[i]; // if we already have some of these parameters in power_profile.xml, ignore the // value in config.xml - if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) { + if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) { continue; } int value = resources.getInteger(configResIds[i]); if (value > 0) { - sPowerMap.put(key, (double) value); + sPowerItemMap.put(key, (double) value); } } } private CpuClusterKey[] mCpuClusters; - private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; - private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster"; - private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster"; + private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; + private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster"; + private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster"; + private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster"; - @SuppressWarnings("deprecation") private void initCpuClusters() { - // Figure out how many CPU clusters we're dealing with - final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT); - if (obj == null || !(obj instanceof Double[])) { + if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) { + final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT); + mCpuClusters = new CpuClusterKey[data.length]; + for (int cluster = 0; cluster < data.length; cluster++) { + int numCpusInCluster = (int) Math.round(data[cluster]); + mCpuClusters[cluster] = new CpuClusterKey( + CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster, + CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster); + } + } else { // Default to single. mCpuClusters = new CpuClusterKey[1]; - mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1); - - } else { - final Double[] array = (Double[]) obj; - mCpuClusters = new CpuClusterKey[array.length]; - for (int cluster = 0; cluster < array.length; cluster++) { - int numCpusInCluster = (int) Math.round(array[cluster]); - mCpuClusters[cluster] = new CpuClusterKey( - POWER_CPU_CLUSTER_SPEED_PREFIX + cluster, - POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster, - numCpusInCluster); + int numCpus = 1; + if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) { + numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT)); } + mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0, + CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus); } } public static class CpuClusterKey { - private final String timeKey; - private final String powerKey; + private final String freqKey; + private final String clusterPowerKey; + private final String corePowerKey; private final int numCpus; - private CpuClusterKey(String timeKey, String powerKey, int numCpus) { - this.timeKey = timeKey; - this.powerKey = powerKey; + private CpuClusterKey(String freqKey, String clusterPowerKey, + String corePowerKey, int numCpus) { + this.freqKey = freqKey; + this.clusterPowerKey = clusterPowerKey; + this.corePowerKey = corePowerKey; this.numCpus = numCpus; } } @@ -354,21 +375,30 @@ public class PowerProfile { return mCpuClusters.length; } - public int getNumCoresInCpuCluster(int index) { - return mCpuClusters[index].numCpus; + public int getNumCoresInCpuCluster(int cluster) { + return mCpuClusters[cluster].numCpus; } - public int getNumSpeedStepsInCpuCluster(int index) { - Object value = sPowerMap.get(mCpuClusters[index].timeKey); - if (value != null && value instanceof Double[]) { - return ((Double[])value).length; + public int getNumSpeedStepsInCpuCluster(int cluster) { + if (cluster < 0 || cluster >= mCpuClusters.length) { + return 0; // index out of bound + } + if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) { + return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length; } return 1; // Only one speed } - public double getAveragePowerForCpu(int cluster, int step) { + public double getAveragePowerForCpuCluster(int cluster) { if (cluster >= 0 && cluster < mCpuClusters.length) { - return getAveragePower(mCpuClusters[cluster].powerKey, step); + return getAveragePower(mCpuClusters[cluster].clusterPowerKey); + } + return 0; + } + + public double getAveragePowerForCpuCore(int cluster, int step) { + if (cluster >= 0 && cluster < mCpuClusters.length) { + return getAveragePower(mCpuClusters[cluster].corePowerKey, step); } return 0; } @@ -379,14 +409,10 @@ public class PowerProfile { * @return the number of memory bandwidth buckets. */ public int getNumElements(String key) { - if (sPowerMap.containsKey(key)) { - Object data = sPowerMap.get(key); - if (data instanceof Double[]) { - final Double[] values = (Double[]) data; - return values.length; - } else { - return 1; - } + if (sPowerItemMap.containsKey(key)) { + return 1; + } else if (sPowerArrayMap.containsKey(key)) { + return sPowerArrayMap.get(key).length; } return 0; } @@ -399,13 +425,10 @@ public class PowerProfile { * @return the average current in milliAmps. */ public double getAveragePowerOrDefault(String type, double defaultValue) { - if (sPowerMap.containsKey(type)) { - Object data = sPowerMap.get(type); - if (data instanceof Double[]) { - return ((Double[])data)[0]; - } else { - return (Double) sPowerMap.get(type); - } + if (sPowerItemMap.containsKey(type)) { + return sPowerItemMap.get(type); + } else if (sPowerArrayMap.containsKey(type)) { + return sPowerArrayMap.get(type)[0]; } else { return defaultValue; } @@ -429,19 +452,16 @@ public class PowerProfile { * @return the average current in milliAmps. */ public double getAveragePower(String type, int level) { - if (sPowerMap.containsKey(type)) { - Object data = sPowerMap.get(type); - if (data instanceof Double[]) { - final Double[] values = (Double[]) data; - if (values.length > level && level >= 0) { - return values[level]; - } else if (level < 0 || values.length == 0) { - return 0; - } else { - return values[values.length - 1]; - } + if (sPowerItemMap.containsKey(type)) { + return sPowerItemMap.get(type); + } else if (sPowerArrayMap.containsKey(type)) { + final Double[] values = sPowerArrayMap.get(type); + if (values.length > level && level >= 0) { + return values[level]; + } else if (level < 0 || values.length == 0) { + return 0; } else { - return (Double) data; + return values[values.length - 1]; } } else { return 0; diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java index c7897b2bbc3b..486b5842400c 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java @@ -26,7 +26,7 @@ public class WakelockPowerCalculator extends PowerCalculator { private long mTotalAppWakelockTimeMs = 0; public WakelockPowerCalculator(PowerProfile profile) { - mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); + mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE); } @Override diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java index 66b777e8e8e6..2b5103377ecb 100644 --- a/core/java/com/android/internal/util/DumpUtils.java +++ b/core/java/com/android/internal/util/DumpUtils.java @@ -102,6 +102,7 @@ public final class DumpUtils { case android.os.Process.ROOT_UID: case android.os.Process.SYSTEM_UID: case android.os.Process.SHELL_UID: + case android.os.Process.INCIDENTD_UID: return true; } diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index f7ea7875c8df..7fd94c6859fb 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,5 +1,6 @@ package com.android.internal.util; +import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -32,8 +33,18 @@ public class ScreenshotHelper { mContext = context; } + /** + * Request a screenshot be taken. + * + * @param screenshotType The type of screenshot, for example either + * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} + * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} + * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not. + * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not. + * @param handler A handler used in case the screenshot times out + */ public void takeScreenshot(final int screenshotType, final boolean hasStatus, - final boolean hasNav, Handler handler) { + final boolean hasNav, @NonNull Handler handler) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 547e83c144a4..e4e46f6d0a8f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1747,6 +1747,12 @@ <permission android:name="android.permission.SEND_EMBMS_INTENTS" android:protectionLevel="signature|privileged" /> + + <!-- Allows internal management of the sensor framework + @hide --> + <permission android:name="android.permission.MANAGE_SENSORS" + android:protectionLevel="signature" /> + <!-- Must be required by an ImsService to ensure that only the system can bind to it. <p>Protection level: signature|privileged @@ -3692,6 +3698,10 @@ <permission android:name="android.permission.READ_RUNTIME_PROFILES" android:protectionLevel="signature|privileged" /> + <!-- @hide Allows audio policy management. --> + <permission android:name="android.permission.MANAGE_AUDIO_POLICY" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to turn on / off quiet mode. @hide <p>Not for use by third-party applications. --> <permission android:name="android.permission.MODIFY_QUIET_MODE" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f084159b3f58..7005e289adf3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -640,6 +640,12 @@ <!-- Wifi driver supports batched scan --> <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool> + <!-- Wifi driver supports Automatic channel selection (ACS) for softap --> + <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool> + + <!-- Wifi driver supports IEEE80211AC for softap --> + <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool> + <!-- Idle Receive current for wifi radio. 0 by default--> <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1993ab4b5924..5309115f37bb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -309,6 +309,8 @@ <java-symbol type="bool" name="config_useFixedVolume" /> <java-symbol type="bool" name="config_forceDefaultOrientation" /> <java-symbol type="bool" name="config_wifi_batched_scan_supported" /> + <java-symbol type="bool" name="config_wifi_softap_acs_supported" /> + <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" /> <java-symbol type="bool" name="config_enableMultiUserUI"/> <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/> <java-symbol type="bool" name="config_hasRecents" /> @@ -1508,6 +1510,7 @@ <java-symbol type="xml" name="password_kbd_symbols" /> <java-symbol type="xml" name="password_kbd_symbols_shift" /> <java-symbol type="xml" name="power_profile" /> + <java-symbol type="xml" name="power_profile_test" /> <java-symbol type="xml" name="sms_short_codes" /> <java-symbol type="xml" name="audio_assets" /> <java-symbol type="xml" name="global_keys" /> diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml new file mode 100644 index 000000000000..cdb71343e5e5 --- /dev/null +++ b/core/res/res/xml/power_profile_test.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2017, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License") +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<device name="Android"> + <!-- All values are in mAh except as noted. + This file is for PowerProfileTest.java. Changes must be synced between these two. Since + power_profile.xml may be overridden by actual device's power_profile.xml at compile time, + this test config ensures we have something constant to test against. Values below are + sample values, not meant to reflect any real device. + --> + + <!-- Nothing --> + <item name="none">0</item> + + <!-- This is the battery capacity in mAh --> + <item name="battery.capacity">3000</item> + + <!-- Number of cores each CPU cluster contains --> + <array name="cpu.clusters.cores"> + <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) --> + <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) --> + </array> + + <!-- Power consumption when CPU is suspended --> + <item name="cpu.suspend">5</item> + <!-- Additional power consumption when CPU is in a kernel idle loop --> + <item name="cpu.idle">1.11</item> + <!-- Additional power consumption by CPU excluding cluster and core when running --> + <item name="cpu.active">2.55</item> + + <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it --> + <item name="cpu.cluster_power.cluster0">2.11</item> + <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it --> + <item name="cpu.cluster_power.cluster1">2.22</item> + + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies --> + <array name="cpu.core_speeds.cluster0"> + <value>300000</value> <!-- 300 MHz CPU speed --> + <value>1000000</value> <!-- 1000 MHz CPU speed --> + <value>2000000</value> <!-- 2000 MHz CPU speed --> + </array> + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies --> + <array name="cpu.core_speeds.cluster1"> + <value>300000</value> <!-- 300 MHz CPU speed --> + <value>1000000</value> <!-- 1000 MHz CPU speed --> + <value>2500000</value> <!-- 2500 MHz CPU speed --> + <value>3000000</value> <!-- 3000 MHz CPU speed --> + </array> + + <!-- Additional power used by a CPU from cluster 0 when running at different + speeds. Currently this measurement also includes cluster cost. --> + <array name="cpu.core_power.cluster0"> + <value>10</value> <!-- 300 MHz CPU speed --> + <value>20</value> <!-- 1000 MHz CPU speed --> + <value>30</value> <!-- 1900 MHz CPU speed --> + </array> + <!-- Additional power used by a CPU from cluster 1 when running at different + speeds. Currently this measurement also includes cluster cost. --> + <array name="cpu.core_power.cluster1"> + <value>25</value> <!-- 300 MHz CPU speed --> + <value>35</value> <!-- 1000 MHz CPU speed --> + <value>50</value> <!-- 2500 MHz CPU speed --> + <value>60</value> <!-- 3000 MHz CPU speed --> + </array> + + <!-- Additional power used when screen is turned on at minimum brightness --> + <item name="screen.on">100</item> + <!-- Additional power used when screen is at maximum brightness, compared to + screen at minimum brightness --> + <item name="screen.full">800</item> + + <!-- Average power used by the camera flash module when on --> + <item name="camera.flashlight">500</item> + <!-- Average power use by the camera subsystem for a typical camera + application. Intended as a rough estimate for an application running a + preview and capturing approximately 10 full-resolution pictures per + minute. --> + <item name="camera.avg">600</item> + + <!-- Additional power used when audio decoding/encoding via DSP --> + <item name="dsp.audio">100</item> + + <!-- Additional power used when GPS is acquiring a signal --> + <item name="gps.on">10</item> + + <!-- Additional power used when cellular radio is transmitting/receiving --> + <item name="radio.active">60</item> + <!-- Additional power used when cellular radio is paging the tower --> + <item name="radio.scanning">3</item> + <!-- Additional power used when the cellular radio is on. Multi-value entry, + one per signal strength (no signal, weak, moderate, strong) --> + <array name="radio.on"> <!-- Strength 0 to BINS-1 --> + <value>6</value> <!-- none --> + <value>5</value> <!-- poor --> + <value>4</value> <!-- moderate --> + <value>3</value> <!-- good --> + <value>3</value> <!-- great --> + </array> +</device> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 37b318065918..ec3a6ce18cce 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -534,7 +534,9 @@ public class SettingsBackupTest { Settings.Secure.VOICE_RECOGNITION_SERVICE, Settings.Secure.INSTANT_APPS_ENABLED, Settings.Secure.BACKUP_MANAGER_CONSTANTS, - Settings.Secure.KEYGUARD_SLICE_URI); + Settings.Secure.KEYGUARD_SLICE_URI, + Settings.Secure.PARENTAL_CONTROL_ENABLED, + Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java index 00732b09f821..4c4aeaf49855 100644 --- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java +++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java @@ -36,20 +36,28 @@ public class SettingsValidatorsTest { @Test public void ensureAllBackedUpSystemSettingsHaveValidators() { - String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP, - Settings.System.VALIDATORS); + String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP, + Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS); failIfOffendersPresent(offenders, "Settings.System"); } @Test public void ensureAllBackedUpGlobalSettingsHaveValidators() { - String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP, - Settings.Global.VALIDATORS); + String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP, + Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS); failIfOffendersPresent(offenders, "Settings.Global"); } + @Test + public void ensureAllBackedUpSecureSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP, + Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS); + + failIfOffendersPresent(offenders, "Settings.Secure"); + } + private void failIfOffendersPresent(String offenders, String settingsType) { if (offenders.length() > 0) { fail("All " + settingsType + " settings that are backed up have to have a non-null" @@ -66,4 +74,16 @@ public class SettingsValidatorsTest { } return offenders.toString(); } + + private String[] concat(String[] first, String[] second) { + if (second == null || second.length == 0) { + return first; + } + final int firstLen = first.length; + final int secondLen = second.length; + String[] both = new String[firstLen + secondLen]; + System.arraycopy(first, 0, both, 0, firstLen); + System.arraycopy(second, 0, both, firstLen, secondLen); + return both; + } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java index b5a7bec75197..32053e30e886 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java @@ -62,9 +62,9 @@ import java.util.Arrays; * * Build: m FrameworksCoreTests * Install: adb install -r \ - * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk + * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner * * or * @@ -73,10 +73,18 @@ import java.util.Arrays; @SmallTest @RunWith(AndroidJUnit4.class) public class BatteryStatsCpuTimesTest { - @Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader; - @Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader; - @Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider; - @Mock PowerProfile mPowerProfile; + @Mock + KernelUidCpuTimeReader mKernelUidCpuTimeReader; + @Mock + KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader; + @Mock + KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader; + @Mock + KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader; + @Mock + BatteryStatsImpl.UserInfoProvider mUserInfoProvider; + @Mock + PowerProfile mPowerProfile; private MockClocks mClocks; private MockBatteryStatsImpl mBatteryStatsImpl; @@ -90,6 +98,8 @@ public class BatteryStatsCpuTimesTest { mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks) .setKernelUidCpuTimeReader(mKernelUidCpuTimeReader) .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader) + .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader) + .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader) .setUserInfoProvider(mUserInfoProvider); } @@ -134,6 +144,10 @@ public class BatteryStatsCpuTimesTest { verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable(); verify(mKernelUidCpuFreqTimeReader).readDelta( any(KernelUidCpuFreqTimeReader.Callback.class)); + verify(mKernelUidCpuActiveTimeReader).readDelta( + any(KernelUidCpuActiveTimeReader.Callback.class)); + verify(mKernelUidCpuClusterTimeReader).readDelta( + any(KernelUidCpuClusterTimeReader.Callback.class)); verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader); for (int i = 0; i < numClusters; ++i) { verify(mKernelCpuSpeedReaders[i]).readDelta(); @@ -228,7 +242,7 @@ public class BatteryStatsCpuTimesTest { updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -301,7 +315,7 @@ public class BatteryStatsCpuTimesTest { when(mUserInfoProvider.exists(testUserId)).thenReturn(true); final int isolatedAppId = FIRST_ISOLATED_UID + 27; final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, isolatedAppId, FIRST_APPLICATION_UID + 33 @@ -389,7 +403,7 @@ public class BatteryStatsCpuTimesTest { final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99); when(mUserInfoProvider.exists(testUserId)).thenReturn(true); when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -431,7 +445,7 @@ public class BatteryStatsCpuTimesTest { updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -514,10 +528,10 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { - FIRST_APPLICATION_UID + 22, - FIRST_APPLICATION_UID + 27, - FIRST_APPLICATION_UID + 33 + final int[] testUids = getUids(testUserId, new int[]{ + FIRST_APPLICATION_UID + 22, + FIRST_APPLICATION_UID + 27, + FIRST_APPLICATION_UID + 33 }); final long[][] uidTimesMs = { {4, 10, 5, 9, 4}, @@ -589,7 +603,7 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -693,7 +707,7 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -782,7 +796,7 @@ public class BatteryStatsCpuTimesTest { } } for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) { - for (int speed = 0 ; speed < clusterFreqs[cluster]; ++speed) { + for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) { assertEquals("There shouldn't be any left-overs: " + Arrays.deepToString(expectedWakeLockUidTimesUs), 0, expectedWakeLockUidTimesUs[cluster][speed]); @@ -797,7 +811,7 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -874,7 +888,7 @@ public class BatteryStatsCpuTimesTest { when(mUserInfoProvider.exists(testUserId)).thenReturn(true); final int isolatedAppId = FIRST_ISOLATED_UID + 27; final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, isolatedAppId, FIRST_APPLICATION_UID + 33 @@ -969,7 +983,7 @@ public class BatteryStatsCpuTimesTest { final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99); when(mUserInfoProvider.exists(testUserId)).thenReturn(true); when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -986,7 +1000,7 @@ public class BatteryStatsCpuTimesTest { callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]); } // And one for the invalid uid - callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21}); + callback.onUidCpuFreqTime(invalidUid, new long[]{12, 839, 32, 34, 21}); return null; }).when(mKernelUidCpuFreqTimeReader).readDelta( any(KernelUidCpuFreqTimeReader.Callback.class)); @@ -1009,6 +1023,136 @@ public class BatteryStatsCpuTimesTest { verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid); } + @Test + public void testReadKernelUidCpuActiveTimesLocked() { + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + + final int testUserId = 11; + when(mUserInfoProvider.exists(testUserId)).thenReturn(true); + final int[] testUids = getUids(testUserId, new int[]{ + FIRST_APPLICATION_UID + 22, + FIRST_APPLICATION_UID + 27, + FIRST_APPLICATION_UID + 33 + }); + final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000}; + doAnswer(invocation -> { + final KernelUidCpuActiveTimeReader.Callback callback = + (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]); + } + return null; + }).when(mKernelUidCpuActiveTimeReader).readDelta( + any(KernelUidCpuActiveTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i], + u.getCpuActiveTime()); + } + + // Repeat the test when the screen is off. + + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000}; + doAnswer(invocation -> { + final KernelUidCpuActiveTimeReader.Callback callback = + (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuActiveTime(testUids[i], deltasMs[i]); + } + return null; + }).when(mKernelUidCpuActiveTimeReader).readDelta( + any(KernelUidCpuActiveTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertEquals("Unexpected cpu active time for uid=" + testUids[i], + uidTimesMs[i] + deltasMs[i], u.getCpuActiveTime()); + } + } + + @Test + public void testReadKernelUidCpuClusterTimesLocked() { + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + + final int testUserId = 11; + when(mUserInfoProvider.exists(testUserId)).thenReturn(true); + final int[] testUids = getUids(testUserId, new int[]{ + FIRST_APPLICATION_UID + 22, + FIRST_APPLICATION_UID + 27, + FIRST_APPLICATION_UID + 33 + }); + final long[][] uidTimesMs = { + {4000, 10000}, + {5000, 1000}, + {8000, 0} + }; + doAnswer(invocation -> { + final KernelUidCpuClusterTimeReader.Callback callback = + (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]); + } + return null; + }).when(mKernelUidCpuClusterTimeReader).readDelta( + any(KernelUidCpuClusterTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i], + u.getCpuClusterTimes()); + } + + // Repeat the test when the screen is off. + + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + final long[][] deltasMs = { + {3000, 12000}, + {3248327490475L, 0}, + {43000, 3345000} + }; + doAnswer(invocation -> { + final KernelUidCpuClusterTimeReader.Callback callback = + (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuPolicyTime(testUids[i], deltasMs[i]); + } + return null; + }).when(mKernelUidCpuClusterTimeReader).readDelta( + any(KernelUidCpuClusterTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], sum(uidTimesMs[i], deltasMs[i]), + u.getCpuClusterTimes()); + } + } + private void updateTimeBasesLocked(boolean unplugged, int screenState, long upTime, long realTime) { // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index e8f24567599e..702f4b8c0dc5 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -39,8 +39,11 @@ import org.junit.runners.Suite; KernelMemoryBandwidthStatsTest.class, KernelSingleUidTimeReaderTest.class, KernelUidCpuFreqTimeReaderTest.class, + KernelUidCpuActiveTimeReaderTest.class, + KernelUidCpuClusterTimeReaderTest.class, KernelWakelockReaderTest.class, - LongSamplingCounterArrayTest.class + LongSamplingCounterArrayTest.class, + PowerProfileTest.class }) public class BatteryStatsTests { } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java new file mode 100644 index 000000000000..1ac82bd1dc96 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java @@ -0,0 +1,240 @@ +/* + * 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.internal.os; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedReader; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Test class for {@link KernelUidCpuActiveTimeReader}. + * + * To run it: + * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest + * + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KernelUidCpuActiveTimeReaderTest { + @Mock private BufferedReader mBufferedReader; + @Mock private KernelUidCpuActiveTimeReader.Callback mCallback; + + private KernelUidCpuActiveTimeReader mReader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mReader = new KernelUidCpuActiveTimeReader(); + } + + public class Temp { + + public void method() { + method1(new long[][]{{1,2,3}, {2,3,4}}); + method1(new long[][]{{2,2,3}, {2,3,4}}); + } + public int method1(long[][] array) { + return array.length * array[0].length; + } + } + + @Test + public void testReadDelta() throws Exception { + final int cores = 8; + final String info = "active: 8"; + final int[] uids = {1, 22, 333, 4444, 5555}; + + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); + } + verifyNoMoreInteractions(mCallback); + + // Verify that a second call will only return deltas. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times1 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that there won't be a callback if the proc file values didn't change. + Mockito.reset(mCallback, mBufferedReader); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that calling with a null callback doesn't result in any crashes + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times1); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, null); + + // Verify that the readDelta call will only return deltas when + // the previous call had null callback. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i = 0; i < uids.length; ++i) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i]))); + } + verifyNoMoreInteractions(mCallback); + } + + @Test + public void testReadDelta_malformedData() throws Exception { + final int cores = 8; + final String info = "active: 8"; + final int[] uids = {1, 22, 333, 4444, 5555}; + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); + } + verifyNoMoreInteractions(mCallback); + + // Verify that there is no callback if subsequent call provides wrong # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] temp = increaseTime(times); + final long[][] times1 = new long[uids.length][]; + for(int i=0;i<temp.length;i++){ + times1[i] = Arrays.copyOfRange(temp[i], 0, 6); + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified if the given core count does not match + // the following # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that there is no callback if any value in the proc file is -ve. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + times3[uids.length - 1][cores - 1] *= -1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i = 0; i < uids.length - 1; ++i) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had -ve value. + Mockito.reset(mCallback, mBufferedReader); + for (int i = 0; i < cores; i++) { + times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1]))); + verifyNoMoreInteractions(mCallback); + + // Verify that there is no callback if the values in the proc file are decreased. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times4 = increaseTime(times3); + times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i = 0; i < uids.length - 1; ++i) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had decreased values. + Mockito.reset(mCallback, mBufferedReader); + for (int i = 0; i < cores; i++) { + times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1]))); + verifyNoMoreInteractions(mCallback); + } + + private long[] subtract(long[] a1, long[] a2) { + long[] val = new long[a1.length]; + for (int i = 0; i < val.length; ++i) { + val[i] = a1[i] - a2[i]; + } + return val; + } + + private String[] formatTime(int[] uids, long[][] times) { + String[] lines = new String[uids.length + 1]; + for (int i=0;i<uids.length;i++){ + StringBuilder sb = new StringBuilder(); + sb.append(uids[i]).append(':'); + for(int j=0;j<times[i].length;j++){ + sb.append(' ').append(times[i][j]); + } + lines[i] = sb.toString(); + } + lines[uids.length] = null; + return lines; + } + + private long[][] increaseTime(long[][] original) { + long[][] newTime = new long[original.length][original[0].length]; + Random rand = new Random(); + for(int i = 0;i<original.length;i++){ + for(int j=0;j<original[0].length;j++){ + newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000; + } + } + return newTime; + } + + private long getTotal(long[] times) { + long sum = 0; + for(int i=0;i<times.length;i++){ + sum+=times[i] * 10 / (i+1); + } + return sum; + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java new file mode 100644 index 000000000000..0d1f85277ebb --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java @@ -0,0 +1,245 @@ +/* + * 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.internal.os; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedReader; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Test class for {@link KernelUidCpuClusterTimeReader}. + * + * To run it: + * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest + * + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KernelUidCpuClusterTimeReaderTest { + @Mock private BufferedReader mBufferedReader; + @Mock private KernelUidCpuClusterTimeReader.Callback mCallback; + + private KernelUidCpuClusterTimeReader mReader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mReader = new KernelUidCpuClusterTimeReader(); + } + + @Test + public void testReadDelta() throws Exception { + final String info = "policy0: 2 policy4: 4"; + final int cores = 6; + final int[] cluster = {2, 4}; + final int[] uids = {1, 22, 333, 4444, 5555}; + + // Verify initial call + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i])); + } + + // Verify that a second call will only return deltas. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times1 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times1[i], times[i]))); + } + + // Verify that there won't be a callback if the proc file values didn't change. + Mockito.reset(mCallback, mBufferedReader); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that calling with a null callback doesn't result in any crashes + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times1); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, null); + + // Verify that the readDelta call will only return deltas when + // the previous call had null callback. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i]))); + } + + } + + @Test + public void testReadDelta_malformedData() throws Exception { + final String info = "policy0: 2 policy4: 4"; + final int cores = 6; + final int[] cluster = {2, 4}; + final int[] uids = {1, 22, 333, 4444, 5555}; + + // Verify initial call + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i])); + } + + // Verify that there is no callback if subsequent call provides wrong # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] temp = increaseTime(times); + final long[][] times1 = new long[uids.length][]; + for(int i=0;i<temp.length;i++){ + times1[i] = Arrays.copyOfRange(temp[i], 0, 4); + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified if the given core count does not match + // the following # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times2[i], times[i]))); + } + + // Verify that there is no callback if any value in the proc file is -ve. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + times3[uids.length - 1][cores - 1] *= -1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length-1;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had -ve value. + Mockito.reset(mCallback, mBufferedReader); + for(int i=0;i<cores;i++){ + times3[uids.length -1][i] = times2[uids.length -1][i] + uids[uids.length -1]*1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback, times(1)).onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1]))); + + // // Verify that there is no callback if the values in the proc file are decreased. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times4 = increaseTime(times3); + times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length-1;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times4[i], times3[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had decreased values. + Mockito.reset(mCallback, mBufferedReader); + for(int i=0;i<cores;i++){ + times4[uids.length -1][i] = times3[uids.length -1][i] + uids[uids.length -1]*1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback, times(1)) + .onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1]))); + + } + + + private long[] subtract(long[] a1, long[] a2) { + long[] val = new long[a1.length]; + for (int i = 0; i < val.length; ++i) { + val[i] = a1[i] - a2[i]; + } + return val; + } + + private String[] formatTime(int[] uids, long[][] times) { + String[] lines = new String[uids.length + 1]; + for (int i=0;i<uids.length;i++){ + StringBuilder sb = new StringBuilder(); + sb.append(uids[i]).append(':'); + for(int j=0;j<times[i].length;j++){ + sb.append(' ').append(times[i][j]); + } + lines[i] = sb.toString(); + } + lines[uids.length] = null; + return lines; + } + + private long[][] increaseTime(long[][] original) { + long[][] newTime = new long[original.length][original[0].length]; + Random rand = new Random(); + for(int i = 0;i<original.length;i++){ + for(int j=0;j<original[0].length;j++){ + newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000; + } + } + return newTime; + } + + // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader + private long[] getTotal(int[] cluster, long[] times) { + int core = 0; + long[] sum = new long[cluster.length]; + for(int i=0;i<cluster.length;i++){ + for(int j=0;j<cluster[i];j++){ + sum[i] += times[core++] * 10 / (j+1); + } + } + return sum; + } + + // Compare array1 against flattened 2d array array2 element by element + private boolean testEqual(long[] array1, long[][] array2) { + int k=0; + for(int i=0;i<array2.length;i++){ + for(int j=0;j<array2[i].length;j++){ + if (k >= array1.length || array1[k++]!=array2[i][j])return false; + } + } + return k == array1.length; + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 6c5a2aac159b..660c744f050d 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -88,6 +88,16 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return this; } + public MockBatteryStatsImpl setKernelUidCpuActiveTimeReader(KernelUidCpuActiveTimeReader reader) { + mKernelUidCpuActiveTimeReader = reader; + return this; + } + + public MockBatteryStatsImpl setKernelUidCpuClusterTimeReader(KernelUidCpuClusterTimeReader reader) { + mKernelUidCpuClusterTimeReader = reader; + return this; + } + public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) { mKernelUidCpuTimeReader = reader; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java new file mode 100644 index 000000000000..eb7da9ca4ffc --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -0,0 +1,58 @@ +/* + * 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.internal.os; + +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; + +import junit.framework.TestCase; + +import org.junit.Before; +import org.junit.Test; + +/* + * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml + */ +@SmallTest +public class PowerProfileTest extends TestCase { + + private PowerProfile mProfile; + + @Before + public void setUp() { + mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true); + } + + @Test + public void testPowerProfile() { + assertEquals(2, mProfile.getNumCpuClusters()); + assertEquals(4, mProfile.getNumCoresInCpuCluster(0)); + assertEquals(4, mProfile.getNumCoresInCpuCluster(1)); + assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND)); + assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)); + assertEquals(2.55, mProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE)); + assertEquals(2.11, mProfile.getAveragePowerForCpuCluster(0)); + assertEquals(2.22, mProfile.getAveragePowerForCpuCluster(1)); + assertEquals(3, mProfile.getNumSpeedStepsInCpuCluster(0)); + assertEquals(30.0, mProfile.getAveragePowerForCpuCore(0, 2)); + assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1)); + assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3)); + assertEquals(3000.0, mProfile.getBatteryCapacity()); + } + +} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 993bae1eea00..c0633cb44e02 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -165,6 +165,9 @@ <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> + <assign-permission name="android.permission.DUMP" uid="incidentd" /> + <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" /> + <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" /> <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" /> diff --git a/docs/html/reference/images/text/style/backgroundcolorspan.png b/docs/html/reference/images/text/style/backgroundcolorspan.png Binary files differnew file mode 100644 index 000000000000..e7e72714c5bb --- /dev/null +++ b/docs/html/reference/images/text/style/backgroundcolorspan.png diff --git a/docs/html/reference/images/text/style/foregroundcolorspan.png b/docs/html/reference/images/text/style/foregroundcolorspan.png Binary files differnew file mode 100644 index 000000000000..f60db6c55254 --- /dev/null +++ b/docs/html/reference/images/text/style/foregroundcolorspan.png diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 05dadc97a5ba..4d715d1cb9ea 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -33,6 +33,8 @@ import android.graphics.drawable.NinePatchDrawable; import android.net.Uri; import android.system.ErrnoException; import android.system.Os; +import android.util.DisplayMetrics; +import android.util.TypedValue; import libcore.io.IoUtils; import dalvik.system.CloseGuard; @@ -64,6 +66,19 @@ public final class ImageDecoder implements AutoCloseable { Resources getResources() { return null; } /* @hide */ + int getDensity() { return Bitmap.DENSITY_NONE; } + + /* @hide */ + int computeDstDensity() { + Resources res = getResources(); + if (res == null) { + return Bitmap.getDefaultDensity(); + } + + return res.getDisplayMetrics().densityDpi; + } + + /* @hide */ abstract ImageDecoder createImageDecoder() throws IOException; }; @@ -170,26 +185,73 @@ public final class ImageDecoder implements AutoCloseable { return decoder; } + private static class InputStreamSource extends Source { + InputStreamSource(Resources res, InputStream is, int inputDensity) { + if (is == null) { + throw new IllegalArgumentException("The InputStream cannot be null"); + } + mResources = res; + mInputStream = is; + mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; + } + + final Resources mResources; + InputStream mInputStream; + final int mInputDensity; + + @Override + public Resources getResources() { return mResources; } + + @Override + public int getDensity() { return mInputDensity; } + + @Override + public ImageDecoder createImageDecoder() throws IOException { + + synchronized (this) { + if (mInputStream == null) { + throw new IOException("Cannot reuse InputStreamSource"); + } + InputStream is = mInputStream; + mInputStream = null; + return createFromStream(is); + } + } + } + private static class ResourceSource extends Source { ResourceSource(Resources res, int resId) { mResources = res; mResId = resId; + mResDensity = Bitmap.DENSITY_NONE; } final Resources mResources; final int mResId; + int mResDensity; @Override public Resources getResources() { return mResources; } @Override + public int getDensity() { return mResDensity; } + + @Override public ImageDecoder createImageDecoder() throws IOException { // This is just used in order to access the underlying Asset and // keep it alive. FIXME: Can we skip creating this object? InputStream is = null; ImageDecoder decoder = null; + TypedValue value = new TypedValue(); try { - is = mResources.openRawResource(mResId); + 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; + } + if (!(is instanceof AssetManager.AssetInputStream)) { // This should never happen. throw new RuntimeException("Resource is not an asset?"); @@ -421,6 +483,22 @@ public final class ImageDecoder implements AutoCloseable { } /** + * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) + * @hide + */ + 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) + * @hide + */ + public static Source createSource(Resources res, InputStream is, int density) { + return new InputStreamSource(res, is, density); + } + + /** * Return the width and height of a given sample size. * * This takes an input that functions like @@ -476,6 +554,10 @@ public final class ImageDecoder implements AutoCloseable { this.resize(dimensions.x, dimensions.y); } + private boolean requestedResize() { + return mWidth != mDesiredWidth || mHeight != mDesiredHeight; + } + // These need to stay in sync with ImageDecoder.cpp's Allocator enum. /** * Use the default allocation for the pixel memory. @@ -730,6 +812,9 @@ public final class ImageDecoder implements AutoCloseable { "Drawable!"); } + // this call potentially manipulates the decoder so it must be performed prior to + // decoding the bitmap and after decode set the density on the resulting bitmap + final int srcDensity = computeDensity(src, decoder); if (decoder.mAnimated) { // AnimatedImageDrawable calls postProcessAndRelease only if // mPostProcess exists. @@ -737,7 +822,8 @@ public final class ImageDecoder implements AutoCloseable { null : decoder; Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, postProcessPtr, decoder.mDesiredWidth, - decoder.mDesiredHeight, decoder.mCropRect, + decoder.mDesiredHeight, srcDensity, + src.computeDstDensity(), decoder.mCropRect, decoder.mInputStream, decoder.mAssetFd); // d has taken ownership of these objects. decoder.mInputStream = null; @@ -746,13 +832,15 @@ public final class ImageDecoder implements AutoCloseable { } Bitmap bm = decoder.decodeBitmap(); - Resources res = src.getResources(); - if (res == null) { - bm.setDensity(Bitmap.DENSITY_NONE); - } + bm.setDensity(srcDensity); + Resources res = src.getResources(); byte[] np = bm.getNinePatchChunk(); if (np != null && NinePatch.isNinePatchChunk(np)) { + if (res != null) { + bm.setDensity(res.getDisplayMetrics().densityDpi); + } + Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); Rect padding = new Rect(); @@ -799,8 +887,46 @@ public final class ImageDecoder implements AutoCloseable { } } - return decoder.decodeBitmap(); + // this call potentially manipulates the decoder so it must be performed prior to + // decoding the bitmap + final int srcDensity = computeDensity(src, decoder); + Bitmap bm = decoder.decodeBitmap(); + bm.setDensity(srcDensity); + return bm; + } + } + + // This method may modify the decoder so it must be called prior to performing the decode + private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) { + // if the caller changed the size then we treat the density as unknown + if (decoder.requestedResize()) { + return Bitmap.DENSITY_NONE; + } + + // Special stuff for compatibility mode: if the target density is not + // the same as the display density, but the resource -is- the same as + // the display density, then don't scale it down to the target density. + // This allows us to load the system's density-correct resources into + // an application in compatibility mode, without scaling those down + // to the compatibility density only to have them scaled back up when + // drawn to the screen. + Resources res = src.getResources(); + final int srcDensity = src.getDensity(); + if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) { + return srcDensity; } + + // downscale the bitmap if the asset has a higher density than the default + final int dstDensity = src.computeDstDensity(); + if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) { + float scale = (float) dstDensity / srcDensity; + int scaledWidth = (int) (decoder.mWidth * scale + 0.5f); + int scaledHeight = (int) (decoder.mHeight * scale + 0.5f); + decoder.resize(scaledWidth, scaledHeight); + return dstDensity; + } + + return srcDensity; } private String getMimeType() { diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index ce3bd9af73b6..da170c0fae24 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -20,12 +20,14 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.AssetFileDescriptor; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.ImageDecoder; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.SystemClock; +import android.util.DisplayMetrics; import libcore.io.IoUtils; import libcore.util.NativeAllocationRegistry; @@ -59,22 +61,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable { * decoder is only non-null if it has a PostProcess */ public AnimatedImageDrawable(long nativeImageDecoder, - @Nullable ImageDecoder decoder, int width, int height, Rect cropRect, + @Nullable ImageDecoder decoder, int width, int height, + int srcDensity, int dstDensity, Rect cropRect, InputStream inputStream, AssetFileDescriptor afd) throws IOException { - mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); - mInputStream = inputStream; - mAssetFd = afd; + width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity); + height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity); if (cropRect == null) { mIntrinsicWidth = width; mIntrinsicHeight = height; } else { + cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity), + Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity), + Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity), + Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity)); mIntrinsicWidth = cropRect.width(); mIntrinsicHeight = cropRect.height(); } - long nativeSize = nNativeByteSize(mNativePtr); + mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); + mInputStream = inputStream; + mAssetFd = afd; + + // FIXME: Use the right size for the native allocation. + long nativeSize = 200; NativeAllocationRegistry registry = new NativeAllocationRegistry( AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); registry.registerNativeAllocation(this, mNativePtr); diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index e3740e3cf284..f74c39d84bce 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Outline; @@ -49,6 +50,7 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @SuppressWarnings("unused") @Deprecated public BitmapDrawable(Resources res) { - mBitmapState = new BitmapState((Bitmap) null); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState((Bitmap) null), res); } /** @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null); + init(new BitmapState(bitmap), null); } /** @@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable { * the display metrics of the resources. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState(bitmap), res); } /** @@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); - } + this(null, filepath); } /** @@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + Bitmap bitmap = null; + try (FileInputStream stream = new FileInputStream(filepath)) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), + (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeFile() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } } } @@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); - } + this(null, is); } /** @@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + Bitmap bitmap = null; + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), + (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeStream() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } } } @@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable { } } + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + Bitmap bitmap = null; try (InputStream is = r.openRawResource(srcResId, value)) { - bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null); + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); } catch (Exception e) { // Do nothing and pick up the error below. } @@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable { } } + private BitmapDrawable(BitmapState state, Resources res) { + init(state, res); + } + /** - * The one constructor to rule them all. This is called by all public + * The one helper to rule them all. This is called by all public & private * constructors to set the state and initialize local properties. */ - private BitmapDrawable(BitmapState state, Resources res) { + private void init(BitmapState state, Resources res) { mBitmapState = state; - updateLocalState(res); + + if (mBitmapState != null && res != null) { + mBitmapState.mTargetDensity = mTargetDensity; + } } /** diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f17cd768c386..291b0a0be4f4 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -50,11 +51,13 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.View; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1175,6 +1178,10 @@ public abstract class Drawable { return null; } + if (opts == null) { + return getBitmapDrawable(res, value, is); + } + /* ugh. The decodeStream contract is that we have already allocated the pad rect, but if the bitmap does not had a ninepatch chunk, then the pad will be ignored. If we could change this to lazily @@ -1207,6 +1214,33 @@ public abstract class Drawable { return null; } + private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) { + try { + ImageDecoder.Source source = null; + if (value != null) { + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + source = ImageDecoder.createSource(res, is, density); + } else { + source = ImageDecoder.createSource(res, is); + } + + return ImageDecoder.decodeDrawable(source, (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); + } catch (IOException e) { + /* do nothing. + If the exception happened on decode, the drawable will be null. + */ + Log.e("Drawable", "Unable to decode stream: " + e); + } + return null; + } + /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see @@ -1306,11 +1340,10 @@ public abstract class Drawable { } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); - try { - Bitmap bm = BitmapFactory.decodeFile(pathName); - if (bm != null) { - return drawableFromBitmap(null, bm, null, null, null, pathName); - } + try (FileInputStream stream = new FileInputStream(pathName)) { + return getBitmapDrawable(null, null, stream); + } catch(IOException e) { + // Do nothing; we will just return null if the FileInputStream had an error } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java index 833376cfa057..603926f4fe4d 100644 --- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java +++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java @@ -19,10 +19,12 @@ package com.android.internal.location.gnssmetrics; import android.os.SystemClock; import android.util.Base64; +import android.util.Log; import android.util.TimeUtils; import java.util.Arrays; +import com.android.internal.app.IBatteryStats; import com.android.internal.location.nano.GnssLogsProto.GnssLog; /** @@ -31,14 +33,29 @@ import com.android.internal.location.nano.GnssLogsProto.GnssLog; */ public class GnssMetrics { + private static final String TAG = GnssMetrics.class.getSimpleName(); + + /* Constant which indicates GPS signal quality is poor */ + public static final int GPS_SIGNAL_QUALITY_POOR = 0; + + /* Constant which indicates GPS signal quality is good */ + public static final int GPS_SIGNAL_QUALITY_GOOD = 1; + + /* Number of GPS signal quality levels */ + public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1; + /** Default time between location fixes (in millisecs) */ private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000; /* The time since boot when logging started */ private String logStartInElapsedRealTime; + /* GNSS power metrics */ + private GnssPowerMetrics mGnssPowerMetrics; + /** Constructor */ - public GnssMetrics() { + public GnssMetrics(IBatteryStats stats) { + mGnssPowerMetrics = new GnssPowerMetrics(stats); locationFailureStatistics = new Statistics(); timeToFirstFixSecStatistics = new Statistics(); positionAccuracyMeterStatistics = new Statistics(); @@ -103,11 +120,18 @@ public class GnssMetrics { * */ public void logCn0(float[] cn0s, int numSv) { - if (numSv < 4) { + if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) { + if (numSv == 0) { + mGnssPowerMetrics.reportSignalQuality(null, 0); + } return; } float[] cn0Array = Arrays.copyOf(cn0s, numSv); Arrays.sort(cn0Array); + mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv); + if (numSv < 4) { + return; + } if (cn0Array[numSv - 4] > 0.0) { double top4AvgCn0 = 0.0; for (int i = numSv - 4; i < numSv; i++) { @@ -265,4 +289,62 @@ public class GnssMetrics { topFourAverageCn0Statistics.reset(); return; } + + /* Class for handling GNSS power related metrics */ + private class GnssPowerMetrics { + + /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */ + private static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0; + + /* Minimum change in Top Four Average CN0 needed to trigger a report */ + private static final double REPORTING_THRESHOLD_DB_HZ = 1.0; + + /* BatteryStats API */ + private final IBatteryStats mBatteryStats; + + /* Last reported Top Four Average CN0 */ + private double mLastAverageCn0; + + public GnssPowerMetrics(IBatteryStats stats) { + mBatteryStats = stats; + // Used to initialize the variable to a very small value (unachievable in practice) so that + // the first CNO report will trigger an update to BatteryStats + mLastAverageCn0 = -100.0; + } + + /** + * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If + * the number of SVs seen is less than 4, then signal quality is the average CN0. + * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ. + */ + public void reportSignalQuality(float[] ascendingCN0Array, int numSv) { + double avgCn0 = 0.0; + if (numSv > 0) { + for (int i = Math.max(0, numSv - 4); i < numSv; i++) { + avgCn0 += (double) ascendingCN0Array[i]; + } + avgCn0 /= Math.min(numSv, 4); + } + if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) { + return; + } + try { + mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0)); + mLastAverageCn0 = avgCn0; + } catch (Exception e) { + Log.w(TAG, "Exception", e); + } + return; + } + + /** + * Obtains signal level based on CN0 + */ + private int getSignalLevel(double cn0) { + if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) { + return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD; + } + return GnssMetrics.GPS_SIGNAL_QUALITY_POOR; + } + } }
\ No newline at end of file diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index e56944dff782..2cd764df2c54 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -914,7 +914,8 @@ public class AudioSystem (1 << STREAM_MUSIC) | (1 << STREAM_RING) | (1 << STREAM_NOTIFICATION) | - (1 << STREAM_SYSTEM); + (1 << STREAM_SYSTEM) | + (1 << STREAM_VOICE_CALL); /** * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes. diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 9ee205f9cde7..2a697b8d5034 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -302,6 +302,7 @@ public class SettingsProvider extends ContentProvider { // fail to boot if there're any backed up settings that don't have a non-null validator ensureAllBackedUpSystemSettingsHaveValidators(); ensureAllBackedUpGlobalSettingsHaveValidators(); + ensureAllBackedUpSecureSettingsHaveValidators(); synchronized (mLock) { mUserManager = UserManager.get(getContext()); @@ -321,19 +322,26 @@ public class SettingsProvider extends ContentProvider { } private void ensureAllBackedUpSystemSettingsHaveValidators() { - String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP, - Settings.System.VALIDATORS); + String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP, + Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS); failToBootIfOffendersPresent(offenders, "Settings.System"); } private void ensureAllBackedUpGlobalSettingsHaveValidators() { - String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP, - Settings.Global.VALIDATORS); + String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP, + Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS); failToBootIfOffendersPresent(offenders, "Settings.Global"); } + private void ensureAllBackedUpSecureSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP, + Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS); + + failToBootIfOffendersPresent(offenders, "Settings.Secure"); + } + private void failToBootIfOffendersPresent(String offenders, String settingsType) { if (offenders.length() > 0) { throw new RuntimeException("All " + settingsType + " settings that are backed up" @@ -352,6 +360,18 @@ public class SettingsProvider extends ContentProvider { return offenders.toString(); } + private final String[] concat(String[] first, String[] second) { + if (second == null || second.length == 0) { + return first; + } + final int firstLen = first.length; + final int secondLen = second.length; + String[] both = new String[firstLen + secondLen]; + System.arraycopy(first, 0, both, 0, firstLen); + System.arraycopy(second, 0, both, firstLen, secondLen); + return both; + } + @Override public Bundle call(String method, String name, Bundle args) { final int requestingUserId = getRequestingUserId(args); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0f43db052622..2a6d55c28ec4 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -135,6 +135,9 @@ <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" /> <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" /> + <uses-permission android:name="android.permission.MANAGE_SENSORS" /> + + <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" /> <application android:label="@string/app_label" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java index 931a99415615..69e347c9476d 100644 --- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java +++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java @@ -463,4 +463,8 @@ public class DataCollector implements SensorEventListener { public boolean isReportingEnabled() { return mAllowReportRejectedTouch; } + + public void onFalsingSessionStarted() { + sessionEntrypoint(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index e4b405f580d4..ed659e2d16d5 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -167,6 +167,9 @@ public class FalsingManager implements SensorEventListener { if (mDataCollector.isEnabledFull()) { registerSensors(COLLECTOR_SENSORS); } + if (mDataCollector.isEnabled()) { + mDataCollector.onFalsingSessionStarted(); + } } private void registerSensors(int [] sensors) { 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 5e08ec3575fc..c9dc25362bb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1704,7 +1704,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mReportRejectedTouch == null) { return; } - mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD + mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); } @@ -4506,6 +4506,7 @@ public class StatusBar extends SystemUI implements DemoMode, ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing); } updateDozingState(); + updateReportRejectedTouchVisibility(); Trace.endSection(); } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index f99ac5c37a66..9b67f8f32704 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5144,6 +5144,11 @@ message MetricsEvent { // OS: P APPLICATIONS_DIRECTORY_ACCESS_DETAIL = 1284; + // OPEN: Settings > Battery > Smart Battery > Restricted apps + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_RESTRICTED_APP_DETAILS = 1285; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java index 3b8d4bca0598..672518cc17ed 100644 --- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java @@ -21,6 +21,8 @@ import android.app.StatusBarManager; import android.content.Context; import android.hardware.input.InputManager; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -30,20 +32,34 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ScreenshotHelper; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; +import java.util.function.Supplier; + /** * Handle the back-end of AccessibilityService#performGlobalAction */ public class GlobalActionPerformer { private final WindowManagerInternal mWindowManagerService; private final Context mContext; + private Supplier<ScreenshotHelper> mScreenshotHelperSupplier; public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) { mContext = context; mWindowManagerService = windowManagerInternal; + mScreenshotHelperSupplier = null; + } + + // Used to mock ScreenshotHelper + @VisibleForTesting + public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal, + Supplier<ScreenshotHelper> screenshotHelperSupplier) { + this(context, windowManagerInternal); + mScreenshotHelperSupplier = screenshotHelperSupplier; } public boolean performGlobalAction(int action) { @@ -79,6 +95,9 @@ public class GlobalActionPerformer { case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: { return lockScreen(); } + case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: { + return takeScreenshot(); + } } return false; } finally { @@ -167,4 +186,12 @@ public class GlobalActionPerformer { mWindowManagerService.lockNow(); return true; } + + private boolean takeScreenshot() { + ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null) + ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext); + screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN, + true, true, new Handler(Looper.getMainLooper())); + return true; + } } diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 518891006b37..465bb09927a5 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -3464,16 +3464,21 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "isAppEligibleForBackup"); - String callerLogString = "BMS.isAppEligibleForBackup"; - TransportClient transportClient = - mTransportManager.getCurrentTransportClient(callerLogString); - boolean eligible = - AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport( - transportClient, packageName, mPackageManager); - if (transportClient != null) { - mTransportManager.disposeOfTransportClient(transportClient, callerLogString); + long oldToken = Binder.clearCallingIdentity(); + try { + String callerLogString = "BMS.isAppEligibleForBackup"; + TransportClient transportClient = + mTransportManager.getCurrentTransportClient(callerLogString); + boolean eligible = + AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport( + transportClient, packageName, mPackageManager); + if (transportClient != null) { + mTransportManager.disposeOfTransportClient(transportClient, callerLogString); + } + return eligible; + } finally { + Binder.restoreCallingIdentity(oldToken); } - return eligible; } @Override @@ -3481,21 +3486,26 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup"); - String callerLogString = "BMS.filterAppsEligibleForBackup"; - TransportClient transportClient = - mTransportManager.getCurrentTransportClient(callerLogString); - List<String> eligibleApps = new LinkedList<>(); - for (String packageName : packages) { - if (AppBackupUtils - .appIsRunningAndEligibleForBackupWithTransport( - transportClient, packageName, mPackageManager)) { - eligibleApps.add(packageName); + long oldToken = Binder.clearCallingIdentity(); + try { + String callerLogString = "BMS.filterAppsEligibleForBackup"; + TransportClient transportClient = + mTransportManager.getCurrentTransportClient(callerLogString); + List<String> eligibleApps = new LinkedList<>(); + for (String packageName : packages) { + if (AppBackupUtils + .appIsRunningAndEligibleForBackupWithTransport( + transportClient, packageName, mPackageManager)) { + eligibleApps.add(packageName); + } } + if (transportClient != null) { + mTransportManager.disposeOfTransportClient(transportClient, callerLogString); + } + return eligibleApps.toArray(new String[eligibleApps.size()]); + } finally { + Binder.restoreCallingIdentity(oldToken); } - if (transportClient != null) { - mTransportManager.disposeOfTransportClient(transportClient, callerLogString); - } - return eligibleApps.toArray(new String[eligibleApps.size()]); } @Override @@ -3514,6 +3524,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } else if ("agents".startsWith(arg)) { dumpAgents(pw); return; + } else if ("transportclients".equals(arg.toLowerCase())) { + mTransportManager.dump(pw); + return; } } } @@ -3576,6 +3589,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } + mTransportManager.dump(pw); + pw.println("Pending init: " + mPendingInits.size()); for (String s : mPendingInits) { pw.println(" " + s); diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 6d9231dcddfa..7e179e5d24f8 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -43,6 +43,7 @@ import com.android.server.backup.transport.TransportConnectionListener; import com.android.server.backup.transport.TransportNotAvailableException; import com.android.server.backup.transport.TransportNotRegisteredException; +import java.io.PrintWriter; import java.util.List; import java.util.Map; import java.util.Set; @@ -633,6 +634,10 @@ public class TransportManager { !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held"); } + public void dump(PrintWriter pw) { + mTransportClientManager.dump(pw); + } + private static Predicate<ComponentName> fromPackageFilter(String packageName) { return transportComponent -> packageName.equals(transportComponent.getPackageName()); } diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java index 399f338d26b2..7b2e3df67942 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportClient.java +++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java @@ -16,6 +16,8 @@ package com.android.server.backup.transport; +import static com.android.server.backup.transport.TransportUtils.formatMessage; + import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.WorkerThread; @@ -28,6 +30,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.EventLog; import android.util.Log; @@ -41,6 +44,9 @@ import com.android.server.backup.TransportManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -65,6 +71,7 @@ import java.util.concurrent.ExecutionException; */ public class TransportClient { private static final String TAG = "TransportClient"; + private static final int LOG_BUFFER_SIZE = 5; private final Context mContext; private final Intent mBindIntent; @@ -73,6 +80,10 @@ public class TransportClient { private final Handler mListenerHandler; private final String mPrefixForLog; private final Object mStateLock = new Object(); + private final Object mLogBufferLock = new Object(); + + @GuardedBy("mLogBufferLock") + private final List<String> mLogBuffer = new LinkedList<>(); @GuardedBy("mStateLock") private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>(); @@ -112,7 +123,7 @@ public class TransportClient { // For logging String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", ""); - mPrefixForLog = classNameForLog + "#" + mIdentifier + ": "; + mPrefixForLog = classNameForLog + "#" + mIdentifier + ":"; } public ComponentName getTransportComponent() { @@ -229,7 +240,7 @@ public class TransportClient { switch (mState) { case State.UNUSABLE: - log(Log.DEBUG, caller, "Async connect: UNUSABLE client"); + log(Log.WARN, caller, "Async connect: UNUSABLE client"); notifyListener(listener, null, caller); break; case State.IDLE: @@ -324,14 +335,14 @@ public class TransportClient { IBackupTransport transport = mTransport; if (transport != null) { - log(Log.DEBUG, caller, "Sync connect: reusing transport"); + log(Log.INFO, caller, "Sync connect: reusing transport"); return transport; } // If it's already UNUSABLE we return straight away, no need to go to main-thread synchronized (mStateLock) { if (mState == State.UNUSABLE) { - log(Log.DEBUG, caller, "Sync connect: UNUSABLE client"); + log(Log.WARN, caller, "Sync connect: UNUSABLE client"); return null; } } @@ -403,13 +414,16 @@ public class TransportClient { } private void notifyListener( - TransportConnectionListener listener, IBackupTransport transport, String caller) { - log(Log.VERBOSE, caller, "Notifying listener of transport = " + transport); + TransportConnectionListener listener, + @Nullable IBackupTransport transport, + String caller) { + String transportString = (transport != null) ? "IBackupTransport" : "null"; + log(Log.INFO, "Notifying [" + caller + "] transport = " + transportString); mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this)); } @GuardedBy("mStateLock") - private void notifyListenersAndClearLocked(IBackupTransport transport) { + private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) { for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) { TransportConnectionListener listener = entry.getKey(); String caller = entry.getValue(); @@ -509,13 +523,30 @@ public class TransportClient { } private void log(int priority, String message) { - TransportUtils.log(priority, TAG, message); + TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message)); + saveLogEntry(formatMessage(null, null, message)); } - private void log(int priority, String caller, String msg) { - TransportUtils.log(priority, TAG, mPrefixForLog, caller, msg); - // TODO(brufino): Log in internal list for dump - // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis()); + private void log(int priority, String caller, String message) { + TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message)); + saveLogEntry(formatMessage(null, caller, message)); + } + + private void saveLogEntry(String message) { + CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis()); + message = time + " " + message; + synchronized (mLogBufferLock) { + if (mLogBuffer.size() == LOG_BUFFER_SIZE) { + mLogBuffer.remove(mLogBuffer.size() - 1); + } + mLogBuffer.add(0, message); + } + } + + List<String> getLogBuffer() { + synchronized (mLogBufferLock) { + return Collections.unmodifiableList(mLogBuffer); + } } @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP}) diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java index 1cbe74716b03..1132bce612b7 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java +++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java @@ -17,19 +17,20 @@ package com.android.server.backup.transport; import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST; +import static com.android.server.backup.transport.TransportUtils.formatMessage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.util.Log; - import com.android.server.backup.TransportManager; +import java.io.PrintWriter; +import java.util.Map; +import java.util.WeakHashMap; /** * Manages the creation and disposal of {@link TransportClient}s. The only class that should use * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}. - * - * <p>TODO(brufino): Implement pool of TransportClients */ public class TransportClientManager { private static final String TAG = "TransportClientManager"; @@ -37,6 +38,7 @@ public class TransportClientManager { private final Context mContext; private final Object mTransportClientsLock = new Object(); private int mTransportClientsCreated = 0; + private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>(); public TransportClientManager(Context context) { mContext = context; @@ -62,8 +64,10 @@ public class TransportClientManager { bindIntent, transportComponent, Integer.toString(mTransportClientsCreated)); + mTransportClientsCallerMap.put(transportClient, caller); mTransportClientsCreated++; - TransportUtils.log(Log.DEBUG, TAG, caller, "Retrieving " + transportClient); + TransportUtils.log( + Log.DEBUG, TAG, formatMessage(null, caller, "Retrieving " + transportClient)); return transportClient; } } @@ -77,7 +81,25 @@ public class TransportClientManager { * details. */ public void disposeOfTransportClient(TransportClient transportClient, String caller) { - TransportUtils.log(Log.DEBUG, TAG, caller, "Disposing of " + transportClient); transportClient.unbind(caller); + synchronized (mTransportClientsLock) { + TransportUtils.log( + Log.DEBUG, TAG, formatMessage(null, caller, "Disposing of " + transportClient)); + mTransportClientsCallerMap.remove(transportClient); + } + } + + public void dump(PrintWriter pw) { + pw.println("Transport clients created: " + mTransportClientsCreated); + synchronized (mTransportClientsLock) { + pw.println("Current transport clients: " + mTransportClientsCallerMap.size()); + for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) { + String caller = mTransportClientsCallerMap.get(transportClient); + pw.println(" " + transportClient + " [" + caller + "]"); + for (String logEntry : transportClient.getLogBuffer()) { + pw.println(" " + logEntry); + } + } + } } } diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java index 92bba9bf06f0..56b2d44ec420 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java +++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java @@ -41,21 +41,20 @@ public class TransportUtils { } static void log(int priority, String tag, String message) { - log(priority, tag, null, message); - } - - static void log(int priority, String tag, @Nullable String caller, String message) { - log(priority, tag, "", caller, message); + if (Log.isLoggable(tag, priority)) { + Slog.println(priority, tag, message); + } } - static void log( - int priority, String tag, String prefix, @Nullable String caller, String message) { - if (Log.isLoggable(tag, priority)) { - if (caller != null) { - prefix += "[" + caller + "] "; - } - Slog.println(priority, tag, prefix + message); + static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) { + StringBuilder string = new StringBuilder(); + if (prefix != null) { + string.append(prefix).append(" "); + } + if (caller != null) { + string.append("[").append(caller).append("] "); } + return string.append(message).toString(); } private TransportUtils() {} diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java index a75a3675f7f9..a538bde7487e 100644 --- a/services/core/java/com/android/server/ForceAppStandbyTracker.java +++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java @@ -737,21 +737,23 @@ public class ForceAppStandbyTracker { * @return whether alarms should be restricted for a UID package-name. */ public boolean areAlarmsRestricted(int uid, @NonNull String packageName) { - return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false); + return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false, + /* exemptOnBatterySaver =*/ false); } /** * @return whether jobs should be restricted for a UID package-name. */ public boolean areJobsRestricted(int uid, @NonNull String packageName) { - return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true); + return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true, + /* exemptOnBatterySaver =*/ false); } /** * @return whether force-app-standby is effective for a UID package-name. */ private boolean isRestricted(int uid, @NonNull String packageName, - boolean useTempWhitelistToo) { + boolean useTempWhitelistToo, boolean exemptOnBatterySaver) { if (isInForeground(uid)) { return false; } @@ -765,12 +767,13 @@ public class ForceAppStandbyTracker { ArrayUtils.contains(mTempWhitelistedAppIds, appId)) { return false; } - - if (mForceAllAppsStandby) { + if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) { return true; } - - return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName); + if (exemptOnBatterySaver) { + return false; + } + return mForceAllAppsStandby; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 29d33ce777f5..c0c684c41bc5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8601,6 +8601,16 @@ public class ActivityManagerService extends IActivityManager.Stub } return false; } + + @Override + public int getPackageUid(String packageName, int flags) { + try { + return mActivityManagerService.mContext.getPackageManager() + .getPackageUid(packageName, flags); + } catch (NameNotFoundException nnfe) { + return -1; + } + } } class IntentFirewallInterface implements IntentFirewall.AMSInterface { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 81e8eb0d4136..207aaa76e5b8 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -40,6 +40,7 @@ import android.os.UserManagerInternal; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.health.HealthStatsParceler; import android.os.health.HealthStatsWriter; import android.os.health.UidHealthStats; @@ -594,6 +595,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + public void noteGpsSignalQuality(int signalLevel) { + synchronized (mStats) { + mStats.noteGpsSignalQualityLocked(signalLevel); + } + } + public void noteScreenState(int state) { enforceCallingPermission(); if (DBG) Slog.d(TAG, "begin noteScreenState"); @@ -1448,6 +1455,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub } /** + * Gets a snapshot of Gps stats + * @hide + */ + public GpsBatteryStats getGpsBatteryStats() { + synchronized (mStats) { + return mStats.getGpsBatteryStats(); + } + } + + /** * Gets a snapshot of the system health for a particular uid. */ @Override diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 799f2a92bc33..f72104981bc0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1047,9 +1047,11 @@ public class AudioService extends IAudioService.Stub private void checkMuteAffectedStreams() { // any stream with a min level > 0 is not muteable by definition + // STREAM_VOICE_CALL can be muted by applications that has the the MODIFY_PHONE_STATE permission. for (int i = 0; i < mStreamStates.length; i++) { final VolumeStreamState vss = mStreamStates[i]; - if (vss.mIndexMin > 0) { + if (vss.mIndexMin > 0 && + vss.mStreamType != AudioSystem.STREAM_VOICE_CALL) { mMuteAffectedStreams &= ~(1 << vss.mStreamType); } } @@ -1412,6 +1414,18 @@ public class AudioService extends IAudioService.Stub return; } + // If adjust is mute and the stream is STREAM_VOICE_CALL, make sure + // that the calling app have the MODIFY_PHONE_STATE permission. + if (isMuteAdjust && + streamType == AudioSystem.STREAM_VOICE_CALL && + mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + // use stream type alias here so that streams with same alias have the same behavior, // including with regard to silent mode control (e.g the use of STREAM_RING below and in // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) @@ -1712,6 +1726,15 @@ public class AudioService extends IAudioService.Stub + " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage); return; } + if ((streamType == AudioManager.STREAM_VOICE_CALL) && + (index == 0) && + (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED)) { + Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without" + + " MODIFY_PHONE_STATE callingPackage=" + callingPackage); + return; + } mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType, index/*val1*/, flags/*val2*/, callingPackage)); setStreamVolume(streamType, index, flags, callingPackage, callingPackage, @@ -4503,27 +4526,30 @@ public class AudioService extends IAudioService.Stub if (mStreamType == srcStream.mStreamType) { return; } - synchronized (VolumeStreamState.class) { - int srcStreamType = srcStream.getStreamType(); - // apply default device volume from source stream to all devices first in case - // some devices are present in this stream state but not in source stream state - int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); - index = rescaleIndex(index, srcStreamType, mStreamType); - for (int i = 0; i < mIndexMap.size(); i++) { - mIndexMap.put(mIndexMap.keyAt(i), index); - } - // Now apply actual volume for devices in source stream state - SparseIntArray srcMap = srcStream.mIndexMap; - for (int i = 0; i < srcMap.size(); i++) { - int device = srcMap.keyAt(i); - index = srcMap.valueAt(i); + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + int srcStreamType = srcStream.getStreamType(); + // apply default device volume from source stream to all devices first in case + // some devices are present in this stream state but not in source stream state + int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); index = rescaleIndex(index, srcStreamType, mStreamType); - - setIndex(index, device, caller); + for (int i = 0; i < mIndexMap.size(); i++) { + mIndexMap.put(mIndexMap.keyAt(i), index); + } + // Now apply actual volume for devices in source stream state + SparseIntArray srcMap = srcStream.mIndexMap; + for (int i = 0; i < srcMap.size(); i++) { + int device = srcMap.keyAt(i); + index = srcMap.valueAt(i); + index = rescaleIndex(index, srcStreamType, mStreamType); + + setIndex(index, device, caller); + } } } } + @GuardedBy("mSettingsLock") public void setAllIndexesToMax() { synchronized (VolumeStreamState.class) { for (int i = 0; i < mIndexMap.size(); i++) { diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index e093c9df7c4b..1ae7d20fb3fe 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -222,7 +222,7 @@ class TunerSession extends ITuner.Stub { MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR); MutableBoolean flagState = new MutableBoolean(false); try { - mHwSession.getConfigFlag(flag, (int result, boolean value) -> { + mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> { halResult.value = result; flagState.value = value; }); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 6dc5403acc99..48d275cc483f 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -827,7 +827,7 @@ public class GnssLocationProvider implements LocationProviderInterface { return isEnabled(); } }; - mGnssMetrics = new GnssMetrics(); + mGnssMetrics = new GnssMetrics(mBatteryStats); } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fba404ed6f0e..6dc384a8831e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -49,6 +49,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -3180,6 +3181,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ SurfaceControl mAppAnimationLayer = null; + /** + * Given that the split-screen divider does not have an AppWindowToken, it + * will have to live inside of a "NonAppWindowContainer", in particular + * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order + * it will need to be interleaved with some of our children, appearing on top of + * both docked stacks but underneath any assistant stacks. + * + * To solve this problem we have this anchor control, which will always exist so + * we can always assign it the correct value in our {@link #assignChildLayers}. + * Likewise since it always exists, {@link AboveAppWindowContainers} can always + * assign the divider a layer relative to it. This way we prevent linking lifecycle + * events between the two containers. + */ + SurfaceControl mSplitScreenDividerAnchor = null; + // Cached reference to some special stacks we tend to get a lot so we don't need to loop // through the list to find them. private TaskStack mHomeStack = null; @@ -3496,37 +3512,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - int layer = 0; - // We allow stacks to change visual order from the AM specified order due to - // Z-boosting during animations. However we must take care to ensure TaskStacks - // which are marked as alwaysOnTop remain that way. - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - s.assignChildLayers(); - if (!s.needsZBoost() && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + final int HOME_STACK_STATE = 0; + final int NORMAL_STACK_STATE = 1; + final int ALWAYS_ON_TOP_STATE = 2; + + int layer = 0; + for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) { + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + if (state == HOME_STACK_STATE && s.isActivityTypeHome()) { + s.assignLayer(t, layer++); + } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome() + && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); + if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) { + t.setLayer(mSplitScreenDividerAnchor, layer++); + } + } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); + } } - } - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - if (s.needsZBoost() && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + // The appropriate place for App-Transitions to occur is right + // above all other animations but still below things in the Picture-and-Picture + // windowing mode. + if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) { + t.setLayer(mAppAnimationLayer, layer++); } } for (int i = 0; i < mChildren.size(); i++) { final TaskStack s = mChildren.get(i); - if (s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); - } + s.assignChildLayers(t); } - // The appropriate place for App-Transitions to occur is right - // above all other animations but still below things in the Picture-and-Picture - // windowing mode. - if (mAppAnimationLayer != null) { - t.setLayer(mAppAnimationLayer, layer++); - } } @Override @@ -3534,6 +3552,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mAppAnimationLayer; } + SurfaceControl getSplitScreenDividerAnchor() { + return mSplitScreenDividerAnchor; + } + @Override void onParentSet() { super.onParentSet(); @@ -3541,11 +3563,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAppAnimationLayer = makeChildSurface(null) .setName("animationLayer") .build(); - getPendingTransaction().show(mAppAnimationLayer); + mSplitScreenDividerAnchor = makeChildSurface(null) + .setName("splitScreenDividerAnchor") + .build(); + getPendingTransaction() + .show(mAppAnimationLayer) + .show(mSplitScreenDividerAnchor); scheduleAnimation(); } else { mAppAnimationLayer.destroy(); mAppAnimationLayer = null; + mSplitScreenDividerAnchor.destroy(); + mSplitScreenDividerAnchor = null; } } } @@ -3560,6 +3589,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo && imeContainer.getSurfaceControl() != null; for (int j = 0; j < mChildren.size(); ++j) { final WindowToken wt = mChildren.get(j); + + // See {@link mSplitScreenDividerAnchor} + if (wt.windowType == TYPE_DOCK_DIVIDER) { + wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1); + continue; + } wt.assignLayer(t, j); wt.assignChildLayers(t); diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java index 66d0da13fff1..429dd8fd1d3d 100644 --- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java @@ -50,13 +50,16 @@ import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.test.mock.MockContentResolver; import android.util.ArraySet; import android.util.Pair; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; +import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.ForceAppStandbyTracker.Listener; import org.junit.Before; @@ -137,6 +140,9 @@ public class ForceAppStandbyTrackerTest { private Consumer<PowerSaveState> mPowerSaveObserver; private BroadcastReceiver mReceiver; + private MockContentResolver mMockContentResolver; + private FakeSettingsProvider mFakeSettingsProvider; + private boolean mPowerSaveMode; private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet(); @@ -182,6 +188,11 @@ public class ForceAppStandbyTrackerTest { any(int[].class) )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>()); + mMockContentResolver = new MockContentResolver(); + mFakeSettingsProvider = new FakeSettingsProvider(); + when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); + mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider); + // Call start. instance.start(); @@ -495,8 +506,8 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); - // Power save on. - mPowerSaveMode = true; + // Updating to the same state should not fire listener + mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); assertNoCallbacks(l); @@ -534,14 +545,14 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); - verify(l, times(0)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); + verify(l, times(1)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); reset(l); setAppOps(UID_10_2, PACKAGE_2, false); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt()); - verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); + verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); @@ -634,10 +645,20 @@ public class ForceAppStandbyTrackerTest { mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); + verify(l, times(1)).updateAllJobs(); + verify(l, times(0)).updateJobsForUid(anyInt()); + verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); + + verify(l, times(0)).unblockAllUnrestrictedAlarms(); + verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); + reset(l); + instance.setPowerSaveWhitelistAppIds(new int[] {UID_1, UID_2}, new int[] {}); waitUntilMainHandlerDrain(); - verify(l, times(1)).updateAllJobs(); + // Called once for updating all whitelist and once for updating temp whitelist + verify(l, times(2)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); @@ -699,7 +720,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -723,7 +744,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -743,6 +764,15 @@ public class ForceAppStandbyTrackerTest { mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); + verify(l, times(1)).updateAllJobs(); + verify(l, times(0)).updateJobsForUid(eq(UID_10_1)); + verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); + + verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); + reset(l); + mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); @@ -751,7 +781,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -775,7 +805,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java index 1213e814b3fa..e72e4601bbe8 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java @@ -16,13 +16,18 @@ package com.android.server.accessibility; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityService; import android.app.StatusBarManager; import android.content.Context; +import android.os.Handler; +import com.android.internal.util.ScreenshotHelper; import com.android.server.wm.WindowManagerInternal; import org.junit.Before; @@ -39,6 +44,7 @@ public class GlobalActionPerformerTest { @Mock Context mMockContext; @Mock WindowManagerInternal mMockWindowManagerInternal; @Mock StatusBarManager mMockStatusBarManager; + @Mock ScreenshotHelper mMockScreenshotHelper; @Before public void setup() { @@ -48,7 +54,8 @@ public class GlobalActionPerformerTest { .thenReturn(mMockStatusBarManager); mGlobalActionPerformer = - new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal); + new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal, + () -> mMockScreenshotHelper); } @Test @@ -70,4 +77,13 @@ public class GlobalActionPerformerTest { mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); verify(mMockWindowManagerInternal).showGlobalActions(); } + + @Test + public void testScreenshot_requestsFromScreenshotHelper() { + mGlobalActionPerformer.performGlobalAction( + AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); + verify(mMockScreenshotHelper).takeScreenshot( + eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(), + anyBoolean(), any(Handler.class)); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index 6468763440a5..5f44fb675330 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -17,14 +17,17 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; @@ -74,11 +77,11 @@ public class ZOrderingTests extends WindowTestsBase { return super.setRelativeLayer(sc, relativeTo, layer); } - int getLayer(SurfaceControl sc) { + private int getLayer(SurfaceControl sc) { return mLayersForControl.getOrDefault(sc, 0); } - SurfaceControl getRelativeLayer(SurfaceControl sc) { + private SurfaceControl getRelativeLayer(SurfaceControl sc) { return mRelativeLayersForControl.get(sc); } }; @@ -146,8 +149,9 @@ public class ZOrderingTests extends WindowTestsBase { return p; } - void assertZOrderGreaterThan(LayerRecordingTransaction t, - SurfaceControl left, SurfaceControl right) throws Exception { + + void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, + SurfaceControl right) throws Exception { final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left); final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right); @@ -171,9 +175,12 @@ public class ZOrderingTests extends WindowTestsBase { } } - void assertWindowLayerGreaterThan(LayerRecordingTransaction t, - WindowState left, WindowState right) throws Exception { - assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl()); + void assertWindowHigher(WindowState left, WindowState right) throws Exception { + assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); + } + + WindowState createWindow(String name) { + return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); } @Test @@ -184,38 +191,37 @@ public class ZOrderingTests extends WindowTestsBase { // The Ime has an higher base layer than app windows and lower base layer than system // windows, so it should be above app windows and below system windows if there isn't an IME // target. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); sWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, "imeAppTargetChildAboveWindow"); @@ -228,41 +234,38 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows except for child windows that are z-ordered above it // and below system windows if it is targeting an app window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception { - final WindowState appBelowImeTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget"); - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); - final WindowState appAboveImeTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget"); + final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); sWm.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for non-fullscreen app window above it and // below system windows if it is targeting an app window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget); - assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, appBelowImeTarget); + assertWindowHigher(appAboveImeTarget, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test @@ -276,20 +279,20 @@ public class ZOrderingTests extends WindowTestsBase { // The IME target base layer is higher than all window except for the nav bar window, so the // IME should be above all windows except for the nav bar. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, imeSystemOverlayTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); // The IME has a higher base layer than the status bar so we may expect it to go // above the status bar once they are both in the Non-App layer, as past versions of this // test enforced. However this seems like the wrong behavior unless the status bar is the // IME target. - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test @@ -297,17 +300,18 @@ public class ZOrderingTests extends WindowTestsBase { sWm.mInputMethodTarget = mStatusBarWindow; mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, mStatusBarWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testStackLayers() throws Exception { + final WindowState anyWindow1 = createWindow("anyWindow"); final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow"); @@ -317,12 +321,22 @@ public class ZOrderingTests extends WindowTestsBase { final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, mDisplayContent, "assistantStackWindow"); + final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION, + mDisplayContent, "homeActivityWindow"); + final WindowState anyWindow2 = createWindow("anyWindow2"); mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow); - assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow); + assertWindowHigher(dockedStackWindow, homeActivityWindow); + assertWindowHigher(assistantStackWindow, homeActivityWindow); + assertWindowHigher(pinnedStackWindow, homeActivityWindow); + assertWindowHigher(anyWindow1, homeActivityWindow); + assertWindowHigher(anyWindow2, homeActivityWindow); + assertWindowHigher(pinnedStackWindow, anyWindow1); + assertWindowHigher(pinnedStackWindow, anyWindow2); + assertWindowHigher(pinnedStackWindow, dockedStackWindow); + assertWindowHigher(pinnedStackWindow, assistantStackWindow); } @Test @@ -337,9 +351,9 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow); - assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow); - assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel); + assertWindowHigher(navBarPanel, mNavBarWindow); + assertWindowHigher(statusBarPanel, mStatusBarWindow); + assertWindowHigher(statusBarSubPanel, statusBarPanel); } @Test @@ -347,8 +361,7 @@ public class ZOrderingTests extends WindowTestsBase { // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side. - final WindowState anyWindow = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow"); + final WindowState anyWindow = createWindow("anyWindow"); final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, "TypeApplicationMediaChild"); final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY, @@ -356,7 +369,29 @@ public class ZOrderingTests extends WindowTestsBase { mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild); - assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child); + assertWindowHigher(anyWindow, mediaOverlayChild); + assertWindowHigher(mediaOverlayChild, child); + } + + @Test + public void testDockedDividerPosition() throws Exception { + final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, + "pinnedStackWindow"); + final WindowState splitScreenWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, + mDisplayContent, "splitScreenWindow"); + final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); + final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, + mDisplayContent, "assistantStackWindow"); + + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(mDockedDividerWindow, splitScreenWindow); + assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow); + assertWindowHigher(pinnedStackWindow, mDockedDividerWindow); } } diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java index 4098b989a16b..0504c79c55bd 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java @@ -62,6 +62,11 @@ public class ServiceManagerPermissionTests extends TestCase { public boolean isRuntimePermission(String permission) { return false; } + + @Override + public int getPackageUid(String packageName, int flags) { + return -1; + } }; ServiceManagerNative.asInterface(BinderInternal.getContextObject()) .setPermissionController(pc); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 6438631cb8ed..2c6011823828 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -267,8 +267,15 @@ public class WifiConfiguration implements Parcelable { public static final int AP_BAND_5GHZ = 1; /** + * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability, + * operating country code and current radio conditions. + * @hide + */ + public static final int AP_BAND_ANY = -1; + + /** * The band which AP resides on - * 0-2G 1-5G + * -1:Any 0:2G 1:5G * By default, 2G is chosen * @hide */ |