diff options
310 files changed, 6164 insertions, 1820 deletions
diff --git a/Android.mk b/Android.mk index c58f7af1d7d5..9bda2dc6d69a 100644 --- a/Android.mk +++ b/Android.mk @@ -32,27 +32,6 @@ ifneq ($(ANDROID_BUILD_EMBEDDED),true) # ============================================================ include $(CLEAR_VARS) -aidl_parcelables := -define stubs-to-aidl-parcelables - gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl - aidl_parcelables += $$(gen) - $$(gen): $(call java-lib-header-files,$1) $(HOST_OUT_EXECUTABLES)/sdkparcelables - @echo Extract SDK parcelables: $$@ - rm -f $$@ - $(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@ -endef - -$(foreach stubs,android_stubs_current android_test_stubs_current android_system_stubs_current,\ - $(eval $(call stubs-to-aidl-parcelables,$(stubs)))) - -gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl -.KATI_RESTAT: $(gen) -$(gen): $(aidl_parcelables) - @echo Combining SDK parcelables: $@ - rm -f $@.tmp - cat $^ | sort -u > $@.tmp - $(call commit-change-for-toc,$@) - # This is used by ide.mk as the list of source files that are # always included. INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document)) diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java index d6e8ab2516a8..884745699789 100644 --- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java +++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java @@ -18,6 +18,7 @@ package android.graphics.perftests; import android.content.Context; import android.content.res.AssetManager; +import android.content.res.Resources; import android.graphics.Typeface; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; @@ -26,6 +27,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.perftests.core.R; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -104,4 +107,15 @@ public class TypefaceCreatePerfTest { outFile.delete(); } + + @Test + public void testCreate_fromResources() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final Resources r = InstrumentationRegistry.getContext().getResources(); + + while (state.keepRunning()) { + Typeface face = r.getFont(R.font.samplefont); + } + } + } diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING index 4d22d0b1f4a3..3a2e5287f01e 100644 --- a/api/TEST_MAPPING +++ b/api/TEST_MAPPING @@ -5,9 +5,11 @@ }, { "name": "CtsSystemApiSignatureTestCases" - }, + } + ], + "imports": [ { - "name": "GtsUnofficialApisUsageTestCases" + "path": "vendor/xts/gts-tests/hostsidetests/api" } ] } diff --git a/api/current.txt b/api/current.txt index 44b11e15f3b8..b7a951c39a62 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1223,8 +1223,8 @@ package android { field public static final int shadowRadius = 16843108; // 0x1010164 field public static final int shape = 16843162; // 0x101019a field public static final int shareInterpolator = 16843195; // 0x10101bb - field public static final int sharedUserId = 16842763; // 0x101000b - field public static final int sharedUserLabel = 16843361; // 0x1010261 + field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b + field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261 field public static final int shell = 16844180; // 0x1010594 field public static final int shortcutDisabledMessage = 16844075; // 0x101052b field public static final int shortcutId = 16844072; // 0x1010528 @@ -27245,7 +27245,7 @@ package android.media.session { method @Nullable public CharSequence getQueueTitle(); method public int getRatingType(); method @Nullable public android.app.PendingIntent getSessionActivity(); - method @Nullable public android.os.Bundle getSessionInfo(); + method @NonNull public android.os.Bundle getSessionInfo(); method @NonNull public android.media.session.MediaSession.Token getSessionToken(); method @NonNull public android.media.session.MediaController.TransportControls getTransportControls(); method public void registerCallback(@NonNull android.media.session.MediaController.Callback); @@ -34415,10 +34415,10 @@ package android.os { method @Deprecated public static String getStorageState(java.io.File); method public static boolean isExternalStorageEmulated(); method public static boolean isExternalStorageEmulated(@NonNull java.io.File); + method public static boolean isExternalStorageLegacy(); + method public static boolean isExternalStorageLegacy(@NonNull java.io.File); method public static boolean isExternalStorageRemovable(); method public static boolean isExternalStorageRemovable(@NonNull java.io.File); - method public static boolean isExternalStorageSandboxed(); - method public static boolean isExternalStorageSandboxed(@NonNull java.io.File); field public static String DIRECTORY_ALARMS; field public static String DIRECTORY_AUDIOBOOKS; field public static String DIRECTORY_DCIM; diff --git a/api/system-current.txt b/api/system-current.txt index d08039d81dab..8cd722b492cd 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1714,7 +1714,6 @@ package android.content.pm { field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 - field public static boolean RESTRICTED_PERMISSIONS_ENABLED; field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 field public static final int RESTRICTION_NONE = 0; // 0x0 @@ -3079,16 +3078,15 @@ package android.location { } public final class GnssCapabilities { - method public boolean hasCapability(int); - field public static final int GEOFENCING = 2; // 0x2 - field public static final int LOW_POWER_MODE = 0; // 0x0 - field public static final int MEASUREMENTS = 3; // 0x3 - field public static final int MEASUREMENT_CORRECTIONS = 5; // 0x5 - field public static final int MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 7; // 0x7 - field public static final int MEASUREMENT_CORRECTIONS_LOS_SATS = 6; // 0x6 - field public static final int MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 8; // 0x8 - field public static final int NAV_MESSAGES = 4; // 0x4 - field public static final int SATELLITE_BLACKLIST = 1; // 0x1 + method public boolean hasGeofencing(); + method public boolean hasLowPowerMode(); + method public boolean hasMeasurementCorrections(); + method public boolean hasMeasurementCorrectionsExcessPathLength(); + method public boolean hasMeasurementCorrectionsLosSats(); + method public boolean hasMeasurementCorrectionsReflectingPane(); + method public boolean hasMeasurements(); + method public boolean hasNavMessages(); + method public boolean hasSatelliteBlacklist(); } public final class GnssMeasurementCorrections implements android.os.Parcelable { @@ -3399,7 +3397,7 @@ package android.location { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); method @Nullable public String getExtraLocationControllerPackage(); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize(); - method @Nullable public android.location.GnssCapabilities getGnssCapabilities(); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.location.GnssCapabilities getGnssCapabilities(); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections); method public boolean isExtraLocationControllerPackageEnabled(); method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle); @@ -5663,16 +5661,16 @@ package android.permission { ctor public PermissionControllerService(); method public final void attachBaseContext(android.content.Context); method @NonNull public final android.os.IBinder onBind(android.content.Intent); - method public abstract int onCountPermissionApps(@NonNull java.util.List<java.lang.String>, int); - method @NonNull public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(@NonNull String); - method @NonNull public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onGetPermissionUsages(boolean, long); - method public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream); - method public abstract void onGrantOrUpgradeDefaultRuntimePermissions(); + method @WorkerThread public abstract int onCountPermissionApps(@NonNull java.util.List<java.lang.String>, int); + method @WorkerThread @NonNull public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(@NonNull String); + method @WorkerThread @NonNull public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onGetPermissionUsages(boolean, long); + method @WorkerThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream); + method @WorkerThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(); method @BinderThread public abstract boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle); method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream); - method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String); - method @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String); - method public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int); + method @WorkerThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String); + method @WorkerThread @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String); + method @WorkerThread public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int); field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; } @@ -6014,17 +6012,6 @@ package android.provider { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String); field public static final String APP_STANDBY_ENABLED = "app_standby_enabled"; field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; - field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs"; - field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; - field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; - field public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; - field public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; - field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2 - field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0 - field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1 - field public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls"; - field public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; - field public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; field public static final String CARRIER_APP_NAMES = "carrier_app_names"; field public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist"; field public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus"; @@ -9479,7 +9466,7 @@ package android.view { package android.view.accessibility { public final class AccessibilityManager { - method public int getAccessibilityWindowId(android.os.IBinder); + method public int getAccessibilityWindowId(@Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut(); } diff --git a/api/test-current.txt b/api/test-current.txt index 63c8df047f90..3cde3f2df435 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2265,17 +2265,6 @@ package android.provider { field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; field public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode"; field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants"; - field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs"; - field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; - field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; - field public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; - field public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; - field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2 - field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0 - field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1 - field public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls"; - field public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; - field public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; field public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold"; field public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled"; field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index fa9a77aa69c6..83c034b4bc42 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -39,6 +39,8 @@ using android::idmap2::CommandLineOptions; using android::idmap2::Error; using android::idmap2::Idmap; +using android::idmap2::kPolicyOdm; +using android::idmap2::kPolicyOem; using android::idmap2::kPolicyProduct; using android::idmap2::kPolicyPublic; using android::idmap2::kPolicySystem; @@ -93,6 +95,8 @@ Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector std::vector<std::string> PoliciesForPath(const std::string& apk_path) { static const std::vector<std::pair<std::string, std::string>> values = { + {"/odm/", kPolicyOdm}, + {"/oem/", kPolicyOem}, {"/product/", kPolicyProduct}, {"/system/", kPolicySystem}, {"/vendor/", kPolicyVendor}, diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp index 8f9a5f848668..9c9b6c754002 100644 --- a/cmds/incidentd/Android.bp +++ b/cmds/incidentd/Android.bp @@ -59,6 +59,12 @@ cc_binary { "libservices", "libutils", "libprotobuf-cpp-lite", + "libcrypto", + "libkeystore_aidl", + "libkeystore_binder", + "libkeystore_parcelables", + "android.hardware.keymaster@4.0", + "libkeymaster4support", ], static_libs: [ @@ -111,6 +117,8 @@ cc_test { "src/incidentd_util.cpp", "src/proto_util.cpp", "src/report_directory.cpp", + "src/cipher/IncidentKeyStore.cpp", + "src/cipher/ProtoEncryption.cpp", "src/**/*.proto", ], @@ -132,6 +140,12 @@ cc_test { "libprotoutil", "libservices", "libutils", + "libcrypto", + "libkeystore_aidl", + "libkeystore_binder", + "libkeystore_parcelables", + "android.hardware.keymaster@4.0", + "libkeymaster4support", ], target: { diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 386303b038e7..91f0dd3a5c35 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -37,6 +37,8 @@ const Privacy* lookup(const Privacy* p, uint32_t fieldId) { return NULL; } +bool sectionEncryption(int section_id) { return section_id == 3025 /*restricted image section*/; } + static bool isAllowed(const uint8_t policy, const uint8_t check) { switch (check) { case PRIVACY_POLICY_LOCAL: diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h index fc8caae7fc60..b599c1c4e344 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -87,6 +87,9 @@ private: uint8_t mPolicy; }; +// TODO: Add privacy flag in incident.proto and auto generate it inside Privacy. +bool sectionEncryption(int section_id); + } // namespace incidentd } // namespace os } // namespace android diff --git a/cmds/incidentd/src/PrivacyFilter.cpp b/cmds/incidentd/src/PrivacyFilter.cpp index 7126322575d5..e8fa4f4ee58e 100644 --- a/cmds/incidentd/src/PrivacyFilter.cpp +++ b/cmds/incidentd/src/PrivacyFilter.cpp @@ -16,15 +16,18 @@ #define DEBUG false #include "Log.h" -#include "incidentd_util.h" #include "PrivacyFilter.h" -#include "proto_util.h" #include <android-base/file.h> -#include <android/util/protobuf.h> #include <android/util/ProtoFileReader.h> +#include <android/util/protobuf.h> #include <log/log.h> +#include "cipher/IncidentKeyStore.h" +#include "cipher/ProtoEncryption.h" +#include "incidentd_util.h" +#include "proto_util.h" + namespace android { namespace os { namespace incidentd { @@ -141,6 +144,8 @@ public: */ status_t writeData(int fd); + sp<ProtoReader> getData() { return mData; } + private: /** * The global set of field --> required privacy level mapping. @@ -247,8 +252,47 @@ void PrivacyFilter::addFd(const sp<FilterFd>& output) { mOutputs.push_back(output); } -status_t PrivacyFilter::writeData(const FdBuffer& buffer, uint8_t bufferLevel, - size_t* maxSize) { +static void write_section_to_file(int sectionId, FieldStripper& fieldStripper, sp<FilterFd> output, + bool encryptIfNeeded) { + status_t err; + + if (sectionEncryption(sectionId) && encryptIfNeeded) { + ProtoEncryptor encryptor(fieldStripper.getData()); + size_t encryptedSize = encryptor.encrypt(); + + if (encryptedSize <= 0) { + output->onWriteError(BAD_VALUE); + return; + } + err = write_section_header(output->getFd(), sectionId, encryptedSize); + VLOG("Encrypted: write section header size %lu", (unsigned long)encryptedSize); + + encryptor.flush(output->getFd()); + + if (err != NO_ERROR) { + output->onWriteError(err); + return; + } + } else { + err = write_section_header(output->getFd(), sectionId, fieldStripper.dataSize()); + VLOG("No encryption: write section header size %lu", + (unsigned long)fieldStripper.dataSize()); + + if (err != NO_ERROR) { + output->onWriteError(err); + return; + } + + err = fieldStripper.writeData(output->getFd()); + if (err != NO_ERROR) { + output->onWriteError(err); + return; + } + } +} + +status_t PrivacyFilter::writeData(const FdBuffer& buffer, uint8_t bufferLevel, size_t* maxSize, + bool encryptIfNeeded) { status_t err; if (maxSize != NULL) { @@ -258,9 +302,9 @@ status_t PrivacyFilter::writeData(const FdBuffer& buffer, uint8_t bufferLevel, // Order the writes by privacy filter, with increasing levels of filtration,k // so we can do the filter once, and then write many times. sort(mOutputs.begin(), mOutputs.end(), - [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool { - return a->getPrivacyPolicy() < b->getPrivacyPolicy(); - }); + [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool { + return a->getPrivacyPolicy() < b->getPrivacyPolicy(); + }); uint8_t privacyPolicy = PRIVACY_POLICY_LOCAL; // a.k.a. no filtering FieldStripper fieldStripper(mRestrictions, buffer.data()->read(), bufferLevel); @@ -279,17 +323,7 @@ status_t PrivacyFilter::writeData(const FdBuffer& buffer, uint8_t bufferLevel, // Write the resultant buffer to the fd, along with the header. ssize_t dataSize = fieldStripper.dataSize(); if (dataSize > 0) { - err = write_section_header(output->getFd(), mSectionId, dataSize); - if (err != NO_ERROR) { - output->onWriteError(err); - continue; - } - - err = fieldStripper.writeData(output->getFd()); - if (err != NO_ERROR) { - output->onWriteError(err); - continue; - } + write_section_to_file(mSectionId, fieldStripper, output, encryptIfNeeded); } if (maxSize != NULL) { @@ -334,14 +368,25 @@ status_t filter_and_write_report(int to, int from, uint8_t bufferLevel, uint32_t fieldId = read_field_id(fieldTag); uint8_t wireType = read_wire_type(fieldTag); if (wireType == WIRE_TYPE_LENGTH_DELIMITED && args.containsSection(fieldId)) { + VLOG("Read section %d", fieldId); // We need this field, but we need to strip it to the level provided in args. PrivacyFilter filter(fieldId, get_privacy_of_section(fieldId)); filter.addFd(new ReadbackFilterFd(args.getPrivacyPolicy(), to)); // Read this section from the reader into an FdBuffer size_t sectionSize = reader->readRawVarint(); + FdBuffer sectionData; - err = sectionData.write(reader, sectionSize); + + // Write data to FdBuffer, if the section was encrypted, decrypt first. + if (sectionEncryption(fieldId)) { + VLOG("sectionSize %lu", (unsigned long)sectionSize); + ProtoDecryptor decryptor(reader, sectionSize); + err = decryptor.decryptAndFlush(§ionData); + } else { + err = sectionData.write(reader, sectionSize); + } + if (err != NO_ERROR) { ALOGW("filter_and_write_report FdBuffer.write failed (this shouldn't happen): %s", strerror(-err)); @@ -349,7 +394,8 @@ status_t filter_and_write_report(int to, int from, uint8_t bufferLevel, } // Do the filter and write. - err = filter.writeData(sectionData, bufferLevel, nullptr); + err = filter.writeData(sectionData, bufferLevel, nullptr, + false /* do not encrypt again*/); if (err != NO_ERROR) { ALOGW("filter_and_write_report filter.writeData had an error: %s", strerror(-err)); return err; @@ -358,6 +404,7 @@ status_t filter_and_write_report(int to, int from, uint8_t bufferLevel, // We don't need this field. Incident does not have any direct children // other than sections. So just skip them. write_field_or_skip(NULL, reader, fieldTag, true); + VLOG("Skip this.... section %d", fieldId); } } diff --git a/cmds/incidentd/src/PrivacyFilter.h b/cmds/incidentd/src/PrivacyFilter.h index 76b28498a0ac..d426db966a9a 100644 --- a/cmds/incidentd/src/PrivacyFilter.h +++ b/cmds/incidentd/src/PrivacyFilter.h @@ -82,8 +82,14 @@ public: * was written (i.e. after filtering). * * The buffer is assumed to have already been filtered to bufferLevel. + * + * This function can be called when persisting data to disk or when sending + * data to client. In the former case, we need to encrypt the data when that + * section requires encryption. In the latter case, we shouldn't send the + * unencrypted data to client. */ - status_t writeData(const FdBuffer& buffer, uint8_t bufferLevel, size_t* maxSize); + status_t writeData(const FdBuffer& buffer, uint8_t bufferLevel, size_t* maxSize, + bool encryptIfNeeded); private: int mSectionId; diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index 218c1b27fdcc..322b97293a26 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -447,7 +447,8 @@ status_t ReportWriter::writeSection(const FdBuffer& buffer) { } }); - return filter.writeData(buffer, PRIVACY_POLICY_LOCAL, &mMaxSectionDataFilteredSize); + return filter.writeData(buffer, PRIVACY_POLICY_LOCAL, &mMaxSectionDataFilteredSize, + true /*encrypt if needed*/); } diff --git a/cmds/incidentd/src/cipher/IncidentKeyStore.cpp b/cmds/incidentd/src/cipher/IncidentKeyStore.cpp new file mode 100644 index 000000000000..ae0a92094d0b --- /dev/null +++ b/cmds/incidentd/src/cipher/IncidentKeyStore.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 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 "IncidentKeyStore.h" + +#include <sys/stat.h> + +static constexpr size_t AES_KEY_BYTES = 32; +static constexpr size_t GCM_MAC_BYTES = 16; +constexpr char kKeyname[] = "IncidentKey"; + +namespace android { +namespace os { +namespace incidentd { + +using namespace keystore; +using std::string; + +IncidentKeyStore& IncidentKeyStore::getInstance() { + static IncidentKeyStore sInstance(new keystore::KeystoreClientImpl); + return sInstance; +} + +bool IncidentKeyStore::encrypt(const string& data, int32_t flags, string* output) { + std::lock_guard<std::mutex> lock(mMutex); + if (data.empty()) { + ALOGW("IncidentKeyStore: Encrypt empty data?!"); + return false; + } + if (!mClient->doesKeyExist(kKeyname)) { + auto gen_result = generateKeyLocked(kKeyname, 0); + if (!gen_result.isOk()) { + ALOGE("IncidentKeyStore: Key generate failed."); + return false; + } + } + if (!mClient->encryptWithAuthentication(kKeyname, data, flags, output)) { + ALOGE("IncidentKeyStore: Encryption failed."); + return false; + } + return true; +} + +bool IncidentKeyStore::decrypt(const std::string& input, string* output) { + std::lock_guard<std::mutex> lock(mMutex); + if (input.empty()) { + ALOGE("IncidentKeyStore: Decrypt empty input?"); + return false; + } + if (!mClient->decryptWithAuthentication(kKeyname, input, output)) { + ALOGE("IncidentKeyStore: Decryption failed."); + return false; + } + return true; +} + +KeyStoreNativeReturnCode IncidentKeyStore::generateKeyLocked(const std::string& name, + int32_t flags) { + auto paramBuilder = AuthorizationSetBuilder() + .AesEncryptionKey(AES_KEY_BYTES * 8) + .GcmModeMinMacLen(GCM_MAC_BYTES * 8) + .Authorization(TAG_NO_AUTH_REQUIRED); + + AuthorizationSet hardware_enforced_characteristics; + AuthorizationSet software_enforced_characteristics; + return mClient->generateKey(name, paramBuilder, flags, &hardware_enforced_characteristics, + &software_enforced_characteristics); +} + +} // namespace incidentd +} // namespace os +} // namespace android diff --git a/cmds/incidentd/src/cipher/IncidentKeyStore.h b/cmds/incidentd/src/cipher/IncidentKeyStore.h new file mode 100644 index 000000000000..27611cd7faad --- /dev/null +++ b/cmds/incidentd/src/cipher/IncidentKeyStore.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 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 + +#include <keystore/keystore_client_impl.h> + +namespace android { +namespace os { +namespace incidentd { + +class IncidentKeyStore { +public: + static IncidentKeyStore& getInstance(); + + IncidentKeyStore(keystore::KeystoreClient* client) : mClient(client) {} + + /** + * Encrypt the plainText and output the encrypted message. + * + * Returns true on success and false otherwise. + * If the key has not been created yet, it will generate the key in KeyMaster. + */ + bool encrypt(const std::string& plainText, int32_t flags, std::string* output); + + /** + * Decrypt and output the decrypted message. + * + * Returns true on success and false otherwise. + */ + bool decrypt(const std::string& encryptedData, std::string* output); + +private: + std::unique_ptr<keystore::KeystoreClient> mClient; + std::mutex mMutex; + keystore::KeyStoreNativeReturnCode generateKeyLocked(const std::string& name, int32_t flags); +}; + +} // namespace incidentd +} // namespace os +} // namespace android diff --git a/cmds/incidentd/src/cipher/ProtoEncryption.cpp b/cmds/incidentd/src/cipher/ProtoEncryption.cpp new file mode 100644 index 000000000000..493796d2af4d --- /dev/null +++ b/cmds/incidentd/src/cipher/ProtoEncryption.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2019 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. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "ProtoEncryption.h" + +#include <android/util/protobuf.h> + +#include "IncidentKeyStore.h" + +namespace android { +namespace os { +namespace incidentd { + +using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_STRING; +using android::util::ProtoOutputStream; +using android::util::ProtoReader; +using std::string; + +static const int FIELD_ID_BLOCK = 1; + +size_t ProtoEncryptor::encrypt() { + string block; + int i = 0; + // Read at most sBlockSize at a time and encrypt. + while (mReader->readBuffer() != NULL) { + size_t readBytes = + mReader->currentToRead() > sBlockSize ? sBlockSize : mReader->currentToRead(); + block.resize(readBytes); + std::memcpy(block.data(), mReader->readBuffer(), readBytes); + + string encrypted; + if (IncidentKeyStore::getInstance().encrypt(block, 0, &encrypted)) { + mOutputStream.write(FIELD_TYPE_STRING | FIELD_ID_BLOCK | FIELD_COUNT_REPEATED, + encrypted); + VLOG("Block %d Encryption: original %lld now %lld", i++, (long long)readBytes, + (long long)encrypted.length()); + mReader->move(readBytes); + } else { + return 0; + } + } + return mOutputStream.size(); +} + +status_t ProtoEncryptor::flush(int fd) { + if (!mOutputStream.flush(fd)) { + return BAD_VALUE; + } + return NO_ERROR; +} + +status_t ProtoDecryptor::readOneBlock(string* output) { + if (!mReader->hasNext()) { + return NO_ERROR; + } + uint64_t fieldTag = mReader->readRawVarint(); + uint32_t fieldId = read_field_id(fieldTag); + uint8_t wireType = read_wire_type(fieldTag); + if (wireType == WIRE_TYPE_LENGTH_DELIMITED) { + // Read this section from the reader into an FdBuffer + size_t sectionSize = mReader->readRawVarint(); + output->resize(sectionSize); + size_t pos = 0; + while (pos < sectionSize && mReader->readBuffer() != NULL) { + size_t toRead = (sectionSize - pos) > mReader->currentToRead() + ? mReader->currentToRead() + : (sectionSize - pos); + std::memcpy(&((output->data())[pos]), mReader->readBuffer(), toRead); + pos += toRead; + mReader->move(toRead); + } + if (pos != sectionSize) { + return BAD_VALUE; + ALOGE("Failed to read one block"); + } + } else { + return BAD_VALUE; + } + return NO_ERROR; +} + +status_t ProtoDecryptor::decryptAndFlush(FdBuffer* out) { + size_t mStartBytes = mReader->bytesRead(); + size_t bytesRead = 0; + int i = 0; + status_t err = NO_ERROR; + // Let's read until we read mTotalSize. If any error occurs before that, make sure to move the + // read pointer so the caller can continue to read the following sections. + while (bytesRead < mTotalSize) { + string block; + err = readOneBlock(&block); + bytesRead = mReader->bytesRead() - mStartBytes; + + if (err != NO_ERROR) { + break; + } + + if (block.length() == 0) { + VLOG("Done reading all blocks"); + break; + } + + string decryptedBlock; + if ((IncidentKeyStore::getInstance()).decrypt(block, &decryptedBlock)) { + VLOG("Block %d Original Size %lu Decrypted size %lu", i++, + (unsigned long)block.length(), (unsigned long)decryptedBlock.length()); + out->write(reinterpret_cast<uint8_t*>(decryptedBlock.data()), decryptedBlock.length()); + } else { + err = BAD_VALUE; + break; + } + } + + if (bytesRead < mTotalSize) { + mReader->move(mTotalSize - bytesRead); + } + return err; +} + +} // namespace incidentd +} // namespace os +} // namespace android diff --git a/cmds/incidentd/src/cipher/ProtoEncryption.h b/cmds/incidentd/src/cipher/ProtoEncryption.h new file mode 100644 index 000000000000..5b72ca88ec64 --- /dev/null +++ b/cmds/incidentd/src/cipher/ProtoEncryption.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 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 + +#include <android/util/ProtoOutputStream.h> +#include <android/util/ProtoReader.h> +#include <frameworks/base/cmds/incidentd/src/cipher/cipher_blocks.pb.h> + +#include "FdBuffer.h" + +namespace android { +namespace os { +namespace incidentd { + +// PlainText IncidentReport format +// [section1_header(id, size, type)][section1_data] ... + +// Let's say section1 needs encryption +// After encryption, it becomes +// [section1_header(id, encrypted_size, type)][[cipher_block][cipher_block][cipher_block]..] + +// When clients read the report, it's decrypted, and written in its original format + +/** + * Takes a ProtoReader, encrypts its whole content -- which is one section, and flush to + * a file descriptor. + * The underlying encryption is done using Keystore binder APIs. We encrypt the data + * in blocks, and write to the file in android.os.incidentd.CipherBlocks format. + */ +class ProtoEncryptor { +public: + ProtoEncryptor(const sp<android::util::ProtoReader>& reader) : mReader(reader){}; + + // Encrypt the data from ProtoReader, and store in CipherBlocks format. + // return the size of CipherBlocks. + size_t encrypt(); + + status_t flush(int fd); + +private: + static const size_t sBlockSize = 8 * 1024; + const sp<android::util::ProtoReader> mReader; + android::util::ProtoOutputStream mOutputStream; +}; + +// Read data from ProtoReader, which is in CipherBlocks proto format. Parse and decrypt +// block by block. +class ProtoDecryptor { +public: + ProtoDecryptor(const sp<android::util::ProtoReader>& reader, size_t size) + : mReader(reader), mTotalSize(size){}; + status_t decryptAndFlush(FdBuffer* out); + +private: + const sp<android::util::ProtoReader> mReader; + + // Total size in bytes we should read from ProtoReader. + const size_t mTotalSize; + + // Read one cipher block from ProtoReader, instead of reading the whole content + // and parse to CipherBlocks which could be huge. + status_t readOneBlock(std::string* output); +}; + +} // namespace incidentd +} // namespace os +} // namespace android diff --git a/cmds/incidentd/src/cipher/cipher_blocks.proto b/cmds/incidentd/src/cipher/cipher_blocks.proto new file mode 100644 index 000000000000..5c7ed242c7a5 --- /dev/null +++ b/cmds/incidentd/src/cipher/cipher_blocks.proto @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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"; + +package android.os.incidentd; + +// This proto is never instantiated anywhere. It only exists to keep a record of the format of the +// encrypted data on disk. +message CipherBlocks { + repeated string blocks = 1; +} diff --git a/cmds/incidentd/tests/IncidentKeyStore_test.cpp b/cmds/incidentd/tests/IncidentKeyStore_test.cpp new file mode 100644 index 000000000000..2250fda90e01 --- /dev/null +++ b/cmds/incidentd/tests/IncidentKeyStore_test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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 "cipher/IncidentKeyStore.h" + +#include <binder/ProcessState.h> +#include <gtest/gtest.h> + +#include <fstream> + +using namespace android::os::incidentd; + +class IncidentKeyStoreTest : public ::testing::Test { +protected: + std::unique_ptr<IncidentKeyStore> incidentKeyStore; + void SetUp() override { + android::ProcessState::self()->startThreadPool(); + incidentKeyStore = std::make_unique<IncidentKeyStore>( + static_cast<keystore::KeystoreClient*>(new keystore::KeystoreClientImpl)); + }; + void TearDown() override { incidentKeyStore = nullptr; }; +}; + +TEST_F(IncidentKeyStoreTest, test_encrypt_decrypt) { + std::string plaintext; + plaintext.resize(4 * 1024, 'a'); + + std::string encrypted; + EXPECT_TRUE(incidentKeyStore->encrypt(plaintext, 0, &encrypted)); + std::string decrypted; + EXPECT_TRUE(incidentKeyStore->decrypt(encrypted, &decrypted)); + + EXPECT_FALSE(encrypted.empty()); + EXPECT_EQ(plaintext, decrypted); +} + +TEST_F(IncidentKeyStoreTest, test_encrypt_empty_hash) { + std::string hash = ""; + + std::string encrypted; + EXPECT_FALSE(incidentKeyStore->encrypt(hash, 0, &encrypted)); + + EXPECT_TRUE(encrypted.empty()); +} + +TEST_F(IncidentKeyStoreTest, test_decrypt_empty_hash) { + std::string hash = ""; + + std::string decrypted; + EXPECT_FALSE(incidentKeyStore->decrypt(hash, &decrypted)); + + EXPECT_TRUE(decrypted.empty()); +}
\ No newline at end of file diff --git a/cmds/incidentd/tests/ProtoEncryption_test.cpp b/cmds/incidentd/tests/ProtoEncryption_test.cpp new file mode 100644 index 000000000000..6742e034d70d --- /dev/null +++ b/cmds/incidentd/tests/ProtoEncryption_test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 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 "cipher/ProtoEncryption.h" + +#include <android-base/file.h> +#include <gtest/gtest.h> + +#include "FdBuffer.h" +#include "android/util/ProtoFileReader.h" + +using namespace android::os::incidentd; +using android::sp; +using std::string; +using ::testing::Test; + +const std::string kTestPath = GetExecutableDirectory(); +const std::string kTestDataPath = kTestPath + "/testdata/"; + +TEST(ProtoEncryptionTest, test_encrypt_decrypt) { + const std::string plaintextFile = kTestDataPath + "plaintext.txt"; + const std::string encryptedFile = kTestDataPath + "encrypted.txt"; + size_t msg1Size = 20 * 1024; + + // Create a file with plain text. + { + unique_fd fd( + open(plaintextFile.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); + ASSERT_NE(fd.get(), -1); + string content; + content.resize(msg1Size, 'a'); + WriteFully(fd, content.data(), msg1Size); + } + + // Read the plain text and encrypted + { + unique_fd readFd(open(plaintextFile.c_str(), O_RDONLY | O_CLOEXEC)); + unique_fd encryptedFd( + open(encryptedFile.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); + + ASSERT_NE(readFd.get(), -1); + ASSERT_NE(encryptedFd.get(), -1); + + sp<ProtoFileReader> reader = new ProtoFileReader(readFd.get()); + ProtoEncryptor encryptor(reader); + EXPECT_TRUE(encryptor.encrypt() > msg1Size); + + encryptor.flush(encryptedFd.get()); + } + + // Read the encrypted file, and decrypt + unique_fd encryptedFd(open(encryptedFile.c_str(), O_RDONLY | O_CLOEXEC)); + ASSERT_NE(encryptedFd.get(), -1); + FdBuffer output; + sp<ProtoFileReader> reader2 = new ProtoFileReader(encryptedFd.get()); + ProtoDecryptor decryptor(reader2, reader2->size()); + decryptor.decryptAndFlush(&output); + + auto decryptedReader = output.data()->read(); + + // Check the content. + int count = 0; + while (decryptedReader->hasNext()) { + if (decryptedReader->next() == 'a') { + count++; + } + } + + EXPECT_EQ(msg1Size, count); +}
\ No newline at end of file diff --git a/cmds/incidentd/tests/ProtoFileReader_test.cpp b/cmds/incidentd/tests/ProtoFileReader_test.cpp new file mode 100644 index 000000000000..acb1bbbb8d23 --- /dev/null +++ b/cmds/incidentd/tests/ProtoFileReader_test.cpp @@ -0,0 +1,100 @@ +// 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. +#define DEBUG false +#include "Log.h" + +#include <android-base/file.h> +#include <android/util/ProtoFileReader.h> +#include <android/util/ProtoOutputStream.h> +#include <android/util/protobuf.h> +#include <fcntl.h> +#include <gtest/gtest.h> +#include <signal.h> +#include <string.h> + +#include "FdBuffer.h" +#include "incidentd_util.h" + +using namespace android; +using namespace android::base; +using namespace android::os::incidentd; +using ::testing::Test; + +const std::string kTestPath = GetExecutableDirectory(); +const std::string kTestDataPath = kTestPath + "/testdata/"; + +status_t read(sp<ProtoFileReader> reader, size_t size) { + uint8_t const* buf; + while (size > 0 && (buf = reader->readBuffer()) != nullptr) { + size_t amt = reader->currentToRead(); + if (size < amt) { + amt = size; + } + reader->move(amt); + size -= amt; + } + + return NO_ERROR; +} + +TEST(ProtoFileReaderTest, ParseOneLevel) { + const std::string testFile = kTestDataPath + "protoFile.txt"; + size_t msg1Size = 10; + size_t msg2Size = 5 * 1024; + { + // Create a proto file + // TestProto { + // optional Section1 section1 = 1; + // optional Section2 section2 = 2; + // } + + unique_fd fd(open(testFile.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); + ASSERT_NE(fd.get(), -1); + ProtoOutputStream proto; + string field1; + field1.resize(msg1Size, 'h'); + string field2; + field2.resize(msg2Size, 'a'); + proto.write(FIELD_TYPE_MESSAGE | 1, field1.data(), field1.length()); + proto.write(FIELD_TYPE_MESSAGE | 2, field2.data(), field2.length()); + proto.flush(fd); + } + + int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC); + ASSERT_NE(fd, -1); + + status_t err; + sp<ProtoFileReader> reader = new ProtoFileReader(fd); + int i = 0; + size_t msg_size[2]; + while (reader->hasNext()) { + uint64_t fieldTag = reader->readRawVarint(); + uint32_t fieldId = read_field_id(fieldTag); + uint8_t wireType = read_wire_type(fieldTag); + ASSERT_EQ(WIRE_TYPE_LENGTH_DELIMITED, wireType); + size_t sectionSize = reader->readRawVarint(); + if (i < 2) { + msg_size[i] = sectionSize; + } + err = read(reader, sectionSize); + ASSERT_EQ(NO_ERROR, err); + i++; + } + + ASSERT_EQ(2, i); + + ASSERT_EQ(msg1Size, msg_size[0]); + ASSERT_EQ(msg2Size, msg_size[1]); + close(fd); +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0d22f3a8bb03..c8bd27503a1c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -127,6 +127,7 @@ import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -723,7 +724,7 @@ public class Activity extends ContextThemeWrapper Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, - AutofillManager.AutofillClient { + AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -1125,6 +1126,12 @@ public class Activity extends ContextThemeWrapper return this; } + /** @hide */ + @Override + public final ContentCaptureClient getContentCaptureClient() { + return this; + } + /** * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives * lifecycle callbacks for only this Activity. @@ -6511,6 +6518,12 @@ public class Activity extends ContextThemeWrapper return getComponentName(); } + /** @hide */ + @Override + public final ComponentName contentCaptureClientGetComponentName() { + return getComponentName(); + } + /** * Retrieve a {@link SharedPreferences} object for accessing preferences * that are private to this activity. This simply calls the underlying diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 13add09bbaaf..9cd42a5eb9fd 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5719,14 +5719,18 @@ public final class ActivityThread extends ClientTransactionHandler { if (packages == null) { break; } + + List<String> packagesHandled = new ArrayList<>(); + synchronized (mResourcesManager) { for (int i = packages.length - 1; i >= 0; i--) { - WeakReference<LoadedApk> ref = mPackages.get(packages[i]); + String packageName = packages[i]; + WeakReference<LoadedApk> ref = mPackages.get(packageName); LoadedApk pkgInfo = ref != null ? ref.get() : null; if (pkgInfo != null) { hasPkgInfo = true; } else { - ref = mResourcePackages.get(packages[i]); + ref = mResourcePackages.get(packageName); pkgInfo = ref != null ? ref.get() : null; if (pkgInfo != null) { hasPkgInfo = true; @@ -5737,8 +5741,8 @@ public final class ActivityThread extends ClientTransactionHandler { // Adjust it's internal references to the application info and // resources. if (pkgInfo != null) { + packagesHandled.add(packageName); try { - final String packageName = packages[i]; final ApplicationInfo aInfo = sPackageManager.getApplicationInfo( packageName, @@ -5770,6 +5774,13 @@ public final class ActivityThread extends ClientTransactionHandler { } } } + + try { + getPackageManager().notifyPackagesReplacedReceived( + packagesHandled.toArray(new String[0])); + } catch (RemoteException ignored) { + } + break; } } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index b93aaa2aca68..40cb29fc80ab 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -236,7 +236,7 @@ public class ResourcesManager { config.screenLayout = Configuration.reduceScreenLayout(sl, config.screenHeightDp, config.screenWidthDp); } - config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate + config.smallestScreenWidthDp = Math.min(config.screenWidthDp, config.screenHeightDp); config.compatScreenWidthDp = config.screenWidthDp; config.compatScreenHeightDp = config.screenHeightDp; config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 98b658d3ad25..c48c878fc366 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1143,7 +1143,7 @@ final class SystemServiceRegistry { Context outerContext = ctx.getOuterContext(); ContentCaptureOptions options = outerContext.getContentCaptureOptions(); // Options is null when the service didn't whitelist the activity or package - if (options != null) { + if (options != null && (options.lite || options.isWhitelisted(outerContext))) { IBinder b = ServiceManager .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b); diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index 76c4fb8caa0b..cb2142c356b2 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -24,6 +24,9 @@ import android.os.Parcelable; import android.util.ArraySet; import android.util.Log; import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; + +import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; @@ -78,12 +81,19 @@ public final class ContentCaptureOptions implements Parcelable { */ public final boolean lite; + /** + * Constructor for "lite" objects that are just used to enable a {@link ContentCaptureManager} + * for contexts belonging to the content capture service app. + */ public ContentCaptureOptions(int loggingLevel) { this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, /* logHistorySize= */ 0, /* whitelistedComponents= */ null); } + /** + * Default constructor. + */ public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, @Nullable ArraySet<ComponentName> whitelistedComponents) { @@ -91,6 +101,16 @@ public final class ContentCaptureOptions implements Parcelable { textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents); } + /** @hide */ + @VisibleForTesting + public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) { + this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE, + ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, + ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, + ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents); + } + private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, @Nullable ArraySet<ComponentName> whitelistedComponents) { @@ -103,10 +123,6 @@ public final class ContentCaptureOptions implements Parcelable { this.whitelistedComponents = whitelistedComponents; } - /** - * @hide - */ - @TestApi public static ContentCaptureOptions forWhitelistingItself() { final ActivityThread at = ActivityThread.currentActivityThread(); if (at == null) { @@ -120,19 +136,27 @@ public final class ContentCaptureOptions implements Parcelable { throw new SecurityException("Thou shall not pass!"); } - final ContentCaptureOptions options = new ContentCaptureOptions( - ContentCaptureManager.LOGGING_LEVEL_VERBOSE, - ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, - ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, - ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, - ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, - /* whitelistedComponents= */ null); + final ContentCaptureOptions options = + new ContentCaptureOptions(/* whitelistedComponents= */ null); // Always log, as it's used by test only Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); return options; } + /** @hide */ + @VisibleForTesting + public boolean isWhitelisted(@NonNull Context context) { + if (whitelistedComponents == null) return true; // whole package is whitelisted + final ContentCaptureClient client = context.getContentCaptureClient(); + if (client == null) { + // Shouldn't happen, but it doesn't hurt to check... + Log.w(TAG, "isWhitelisted(): no ContentCaptureClient on " + context); + return false; + } + return whitelistedComponents.contains(client.contentCaptureClientGetComponentName()); + } + @Override public String toString() { if (lite) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index de048290c0ca..00238bfe0ee3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -70,6 +70,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.WindowManager; import android.view.autofill.AutofillManager.AutofillClient; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.view.textclassifier.TextClassificationManager; import java.io.File; @@ -5414,6 +5415,14 @@ public abstract class Context { /** * @hide */ + @Nullable + public ContentCaptureClient getContentCaptureClient() { + return null; + } + + /** + * @hide + */ public final boolean isAutofillCompatibilityEnabled() { final AutofillOptions options = getAutofillOptions(); return options != null && options.compatModeEnabled; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e66cd31a0641..389832815d77 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -39,7 +39,6 @@ import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; -import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -8609,6 +8608,13 @@ public class Intent implements Parcelable, Cloneable { * fields, the identifier is <em>never</em> used for matching against an {@link IntentFilter}; * it is as if the identifier has not been set on the Intent. * + * <p>This can be used, for example, to make this Intent unique from other Intents that + * are otherwise the same, for use in creating a {@link android.app.PendingIntent}. (Be aware + * however that the receiver of the PendingIntent will see whatever you put in here.) The + * structure of this string is completely undefined by the platform, however if you are going + * to be exposing identifier strings across different applications you may need to define + * your own structure if there is no central party defining the contents of this field.</p> + * * @param identifier The identifier for this Intent. The contents of the string have no * meaning to the system, except whether they are exactly the same as * another identifier. @@ -10181,6 +10187,9 @@ public class Intent implements Parcelable, Cloneable { if (mType != null) { proto.write(IntentProto.TYPE, mType); } + if (mIdentifier != null) { + proto.write(IntentProto.IDENTIFIER, mIdentifier); + } if (mFlags != 0) { proto.write(IntentProto.FLAG, "0x" + Integer.toHexString(mFlags)); } diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java index 639335e53ae5..1838babf7794 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -45,8 +45,7 @@ public final class OverlayInfo implements Parcelable { STATE_DISABLED, STATE_ENABLED, STATE_ENABLED_STATIC, - // @Deprecated STATE_TARGET_UPGRADING, - STATE_TARGET_IS_BEING_REPLACED, + // @Deprecated STATE_TARGET_IS_BEING_REPLACED, STATE_OVERLAY_IS_BEING_REPLACED, }) /** @hide */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index cf704d52cba0..6ab4657d727d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -770,4 +770,6 @@ interface IPackageManager { int getRuntimePermissionsVersion(int userId); void setRuntimePermissionsVersion(int version, int userId); + + void notifyPackagesReplacedReceived(in String[] packages); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 83e15e8daa39..5f3e0570840a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -87,8 +87,8 @@ public abstract class PackageManager { public static final boolean APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE = true; /** {@hide} */ - @SystemApi @TestApi + // STOPSHIP: Remove this once we get a Play prebuilt. public static boolean RESTRICTED_PERMISSIONS_ENABLED = false; /** @@ -2030,9 +2030,10 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and - * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API - * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate - * what level of optional hardware features limits it supports. + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan + * implementation on this device is hardware accelerated, and the Vulkan native API will + * enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate what + * level of optional hardware features limits it supports. * <p> * Level 0 includes the base Vulkan requirements as well as: * <ul><li>{@code VkPhysicalDeviceFeatures::textureCompressionETC2}</li></ul> @@ -2057,10 +2058,10 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and - * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API - * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate - * what level of optional compute features that device supports beyond the Vulkan 1.0 - * requirements. + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan + * implementation on this device is hardware accelerated, and the Vulkan native API will + * enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate what + * level of optional compute features that device supports beyond the Vulkan 1.0 requirements. * <p> * Compute level 0 indicates: * <ul> @@ -2075,10 +2076,11 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and - * {@link #hasSystemFeature(String, int)}: The version of this feature indicates the highest - * {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices that support - * the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The feature version - * uses the same encoding as Vulkan version numbers: + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan + * implementation on this device is hardware accelerated, and the feature version will indicate + * the highest {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices + * that support the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The + * feature version uses the same encoding as Vulkan version numbers: * <ul> * <li>Major version number in bits 31-22</li> * <li>Minor version number in bits 21-12</li> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 3ffc21d2c6e3..04e8cf421baa 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -609,16 +609,17 @@ public abstract class CameraMetadata<TKey> { /** * <p>The camera device supports capturing high-resolution images at >= 20 frames per - * second, in at least the uncompressed YUV format, when post-processing settings are set - * to FAST. Additionally, maximum-resolution images can be captured at >= 10 frames - * per second. Here, 'high resolution' means at least 8 megapixels, or the maximum - * resolution of the device, whichever is smaller.</p> + * second, in at least the uncompressed YUV format, when post-processing settings are + * set to FAST. Additionally, all image resolutions less than 24 megapixels can be + * captured at >= 10 frames per second. Here, 'high resolution' means at least 8 + * megapixels, or the maximum resolution of the device, whichever is smaller.</p> * <p>More specifically, this means that a size matching the camera device's active array * size is listed as a supported size for the {@link android.graphics.ImageFormat#YUV_420_888 } format in either {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } or {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes }, * with a minimum frame duration for that format and size of either <= 1/20 s, or - * <= 1/10 s, respectively; and the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry - * lists at least one FPS range where the minimum FPS is >= 1 / minimumFrameDuration - * for the maximum-size YUV_420_888 format. If that maximum size is listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes }, + * <= 1/10 s if the image size is less than 24 megapixels, respectively; and + * the {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} entry lists at least one FPS range + * where the minimum FPS is >= 1 / minimumFrameDuration for the maximum-size + * YUV_420_888 format. If that maximum size is listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getHighResolutionOutputSizes }, * then the list of resolutions for YUV_420_888 from {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } contains at * least one resolution >= 8 megapixels, with a minimum frame duration of <= 1/20 * s.</p> diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 996f9978a612..bdba77cc880e 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -838,8 +838,10 @@ public final class StreamConfigurationMap { * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE} * capability. This does not include the stall duration, so for example, a JPEG or RAW16 output * resolution with a large stall duration but a minimum frame duration that's above 20 fps will - * still be listed in the regular {@link #getOutputSizes} list. All the sizes on this list are - * still guaranteed to operate at a rate of at least 10 fps, not including stall duration.</p> + * still be listed in the regular {@link #getOutputSizes} list. All the sizes on this list that + * are less than 24 megapixels are still guaranteed to operate at a rate of at least 10 fps, + * not including stall duration. Sizes on this list that are at least 24 megapixels are allowed + * to operate at less than 10 fps.</p> * * <p>For a device that does not support the BURST_CAPTURE capability, this list will be * {@code null}, since resolutions in the {@link #getOutputSizes} list are already not diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2906710b0655..0e10de8c4e3f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -510,7 +510,7 @@ public class ConnectivityManager { * The absence of a connection type. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public static final int TYPE_NONE = -1; /** @@ -627,7 +627,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public static final int TYPE_MOBILE_FOTA = 10; /** @@ -645,7 +645,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public static final int TYPE_MOBILE_CBS = 12; /** @@ -655,7 +655,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public static final int TYPE_WIFI_P2P = 13; /** @@ -674,7 +674,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public static final int TYPE_MOBILE_EMERGENCY = 15; /** @@ -775,7 +775,7 @@ public class ConnectivityManager { */ public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) private final IConnectivityManager mService; /** * A kludge to facilitate static access where a Context pointer isn't available, like in the @@ -867,7 +867,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public static boolean isNetworkTypeMobile(int networkType) { switch (networkType) { case TYPE_MOBILE: @@ -1304,7 +1304,7 @@ public class ConnectivityManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public LinkProperties getLinkProperties(int networkType) { try { return mService.getLinkPropertiesForType(networkType); @@ -3042,7 +3042,7 @@ public class ConnectivityManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) public boolean isNetworkSupported(int networkType) { try { return mService.isNetworkSupported(networkType); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 4332d8abbce3..1c6a48434adc 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -25,6 +25,7 @@ import android.app.backup.BackupManager; import android.app.usage.NetworkStatsManager; import android.content.Context; import android.media.MediaPlayer; +import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; import android.util.DataUnit; @@ -169,7 +170,7 @@ public class TrafficStats { private static INetworkStatsService sStatsService; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) private synchronized static INetworkStatsService getStatsService() { if (sStatsService == null) { sStatsService = INetworkStatsService.Stub.asInterface( @@ -979,7 +980,7 @@ public class TrafficStats { * Interfaces are never removed from this list, so counters should always be * monotonic. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) private static String[] getMobileIfaces() { try { return getStatsService().getMobileIfaces(); diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java index 5b674067192e..cf08b653d0f4 100644 --- a/core/java/android/net/UrlQuerySanitizer.java +++ b/core/java/android/net/UrlQuerySanitizer.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Locale; import java.util.Set; import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @@ -837,15 +839,11 @@ public class UrlQuerySanitizer { * @param string the escaped string * @return the unescaped string. */ + private static final Pattern plusOrPercent = Pattern.compile("[+%]"); public String unescape(String string) { - // Early exit if no escaped characters. - int firstEscape = string.indexOf('%'); - if ( firstEscape < 0) { - firstEscape = string.indexOf('+'); - if (firstEscape < 0) { - return string; - } - } + final Matcher matcher = plusOrPercent.matcher(string); + if (!matcher.find()) return string; + final int firstEscape = matcher.start(); int length = string.length(); @@ -855,8 +853,7 @@ public class UrlQuerySanitizer { char c = string.charAt(i); if (c == '+') { c = ' '; - } - else if ( c == '%' && i + 2 < length) { + } else if (c == '%' && i + 2 < length) { char c1 = string.charAt(i + 1); char c2 = string.charAt(i + 2); if (isHexDigit(c1) && isHexDigit(c2)) { diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index dde1e6a7f5f7..e85561c7cb9f 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -1115,42 +1115,42 @@ public class Environment { } /** - * Returns whether the shared/external storage media at the given path is a - * sandboxed view that only contains files owned by the app. + * Returns whether the primary shared/external storage media is a legacy + * view that includes files not owned by the app. * <p> * This value may be different from the value requested by - * {@code allowExternalStorageSandbox} in the app's manifest, since an app - * may inherit its sandboxed state based on when it was first installed. + * {@code requestLegacyExternalStorage} in the app's manifest, since an app + * may inherit its legacy state based on when it was first installed. * <p> - * Sandboxed apps can continue to discover and read media belonging to other - * apps via {@link android.provider.MediaStore}. + * Non-legacy apps can continue to discover and read media belonging to + * other apps via {@link android.provider.MediaStore}. */ - public static boolean isExternalStorageSandboxed() { + public static boolean isExternalStorageLegacy() { final File externalDir = sCurrentUser.getExternalDirs()[0]; - return isExternalStorageSandboxed(externalDir); + return isExternalStorageLegacy(externalDir); } /** * Returns whether the shared/external storage media at the given path is a - * sandboxed view that only contains files owned by the app. + * legacy view that includes files not owned by the app. * <p> * This value may be different from the value requested by - * {@code allowExternalStorageSandbox} in the app's manifest, since an app - * may inherit its sandboxed state based on when it was first installed. + * {@code requestLegacyExternalStorage} in the app's manifest, since an app + * may inherit its legacy state based on when it was first installed. * <p> - * Sandboxed apps can continue to discover and read media belonging to other - * apps via {@link android.provider.MediaStore}. + * Non-legacy apps can continue to discover and read media belonging to + * other apps via {@link android.provider.MediaStore}. * * @throws IllegalArgumentException if the path is not a valid storage * device. */ - public static boolean isExternalStorageSandboxed(@NonNull File path) { + public static boolean isExternalStorageLegacy(@NonNull File path) { final Context context = AppGlobals.getInitialApplication(); final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, context.getApplicationInfo().uid, - context.getOpPackageName()) != AppOpsManager.MODE_ALLOWED; + context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; } static File getDirectory(String variableName, String defaultPath) { diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index a51a871e7780..232869d7aefc 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -453,14 +453,22 @@ public class GraphicsEnvironment { final boolean appIsProfileable = isProfileable(context); final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1; if (appIsDebuggable || appIsProfileable || deviceIsDebuggable) { - - String debugPackage = - coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE); + String debugPackage; + + if (coreSettings != null) { + debugPackage = + coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE); + } else { + ContentResolver contentResolver = context.getContentResolver(); + debugPackage = Settings.Global.getString(contentResolver, + Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE); + } if ((debugPackage != null) && (!debugPackage.isEmpty())) { return debugPackage; } } + return ""; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 7d61bf6f3986..2fff595d7150 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -555,6 +555,18 @@ public final class PowerManager { } /** + * @hide + */ + public static class WakeData { + public WakeData(long wakeTime, @WakeReason int wakeReason) { + this.wakeTime = wakeTime; + this.wakeReason = wakeReason; + } + public long wakeTime; + public @WakeReason int wakeReason; + } + + /** * The value to pass as the 'reason' argument to reboot() to reboot into * recovery mode for tasks other than applying system updates, such as * doing factory resets. diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index d55489a08136..9661a08464dd 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -203,4 +203,7 @@ public abstract class PowerManagerInternal { /** Returns whether there hasn't been a user activity event for the given number of ms. */ public abstract boolean wasDeviceIdleFor(long ms); + + /** Returns information about the last wakeup event. */ + public abstract PowerManager.WakeData getLastWakeup(); } diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index ed44367e28c6..fd7c8739dc60 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -28,18 +28,19 @@ import static com.android.internal.util.Preconditions.checkCollectionElementsNot import static com.android.internal.util.Preconditions.checkFlagsArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkStringNotEmpty; -import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest; import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.WorkerThread; import android.app.Service; import android.app.admin.DevicePolicyManager.PermissionGrantState; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -96,6 +97,7 @@ public abstract class PermissionControllerService extends Service { * * @return the actually removed permissions as {@code Map<packageName, List<permission>>} */ + @WorkerThread public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions( @NonNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName); @@ -106,6 +108,7 @@ public abstract class PermissionControllerService extends Service { * @param user The user to back up * @param backup The stream to write the backup to */ + @WorkerThread public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user, @NonNull OutputStream backup); @@ -142,6 +145,7 @@ public abstract class PermissionControllerService extends Service { * * @return descriptions of the runtime permissions of the app */ + @WorkerThread public abstract @NonNull List<RuntimePermissionPresentationInfo> onGetAppPermissions( @NonNull String packageName); @@ -151,6 +155,7 @@ public abstract class PermissionControllerService extends Service { * @param packageName The package for which to revoke * @param permissionName The permission to revoke */ + @WorkerThread public abstract void onRevokeRuntimePermission(@NonNull String packageName, @NonNull String permissionName); @@ -163,6 +168,7 @@ public abstract class PermissionControllerService extends Service { * * @return the number of apps that have one of the permissions */ + @WorkerThread public abstract int onCountPermissionApps(@NonNull List<String> permissionNames, @CountPermissionAppsFlag int flags); @@ -174,6 +180,7 @@ public abstract class PermissionControllerService extends Service { * * @return descriptions of the users of permissions */ + @WorkerThread public abstract @NonNull List<RuntimePermissionUsageInfo> onGetPermissionUsages( boolean countSystem, long numMillis); @@ -186,6 +193,7 @@ public abstract class PermissionControllerService extends Service { * @see PermissionManager#getRuntimePermissionsVersion() * @see PermissionManager#setRuntimePermissionsVersion(int) */ + @WorkerThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(); /** @@ -196,6 +204,7 @@ public abstract class PermissionControllerService extends Service { * @param permission Permission to change * @param grantState State to set the permission into */ + @WorkerThread public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin( @NonNull String callerPackageName, @NonNull String packageName, @NonNull String permission, @PermissionGrantState int grantState); @@ -232,10 +241,9 @@ public abstract class PermissionControllerService extends Service { throw new RuntimeException(e); } - mHandler.sendMessage(obtainMessage( - PermissionControllerService::revokeRuntimePermissions, - PermissionControllerService.this, request, doDryRun, reason, - callerPackageName, callback)); + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> + PermissionControllerService.this.revokeRuntimePermissions(request, doDryRun, + reason, callerPackageName, callback)); } @Override @@ -245,9 +253,8 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); - mHandler.sendMessage(obtainMessage( - PermissionControllerService::getRuntimePermissionsBackup, - PermissionControllerService.this, user, pipe)); + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> + PermissionControllerService.this.getRuntimePermissionsBackup(user, pipe)); } @Override @@ -287,9 +294,9 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); - mHandler.sendMessage( - obtainMessage(PermissionControllerService::getAppPermissions, - PermissionControllerService.this, packageName, callback)); + AsyncTask.THREAD_POOL_EXECUTOR.execute( + () -> PermissionControllerService.this.getAppPermissions(packageName, + callback)); } @Override @@ -299,9 +306,9 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); - mHandler.sendMessage( - obtainMessage(PermissionControllerService::onRevokeRuntimePermission, - PermissionControllerService.this, packageName, permissionName)); + AsyncTask.THREAD_POOL_EXECUTOR.execute( + () -> PermissionControllerService.this.onRevokeRuntimePermission( + packageName, permissionName)); } @Override @@ -313,10 +320,9 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); - mHandler.sendMessage( - obtainMessage(PermissionControllerService::countPermissionApps, - PermissionControllerService.this, permissionNames, flags, - callback)); + AsyncTask.THREAD_POOL_EXECUTOR.execute( + () -> PermissionControllerService.this.countPermissionApps(permissionNames, + flags, callback)); } @Override @@ -327,10 +333,9 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); - mHandler.sendMessage( - obtainMessage(PermissionControllerService::getPermissionUsages, - PermissionControllerService.this, countSystem, numMillis, - callback)); + AsyncTask.THREAD_POOL_EXECUTOR.execute( + () -> PermissionControllerService.this.getPermissionUsages(countSystem, + numMillis, callback)); } @Override @@ -356,10 +361,10 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, null); - mHandler.sendMessage(obtainMessage( - PermissionControllerService::setRuntimePermissionGrantStateByDeviceAdmin, - PermissionControllerService.this, callerPackageName, packageName, - permission, grantState, callback)); + AsyncTask.THREAD_POOL_EXECUTOR.execute( + () -> PermissionControllerService.this + .setRuntimePermissionGrantStateByDeviceAdmin(callerPackageName, + packageName, permission, grantState, callback)); } @Override @@ -369,9 +374,9 @@ public abstract class PermissionControllerService extends Service { enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, null); - mHandler.sendMessage(obtainMessage( - PermissionControllerService::grantOrUpgradeDefaultRuntimePermissions, - PermissionControllerService.this, callback)); + AsyncTask.THREAD_POOL_EXECUTOR.execute( + () -> PermissionControllerService.this + .grantOrUpgradeDefaultRuntimePermissions(callback)); } }; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ac59101d4090..24f42d49b243 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11124,8 +11124,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; /** @@ -11134,8 +11132,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; /** @@ -11144,8 +11140,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; /** @@ -11155,8 +11149,6 @@ public final class Settings { * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; /** @@ -11185,8 +11177,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; /** @@ -11195,8 +11185,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; /** @@ -11205,8 +11193,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; /** @@ -11215,8 +11201,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls"; @@ -11226,8 +11210,6 @@ public final class Settings { * by "@@,@@". * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs"; @@ -11238,8 +11220,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; /** @@ -11248,8 +11228,6 @@ public final class Settings { * * @hide */ - @SystemApi - @TestApi public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; /** @@ -14712,6 +14690,18 @@ public final class Settings { public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS = "text_classifier_action_model_params"; + /** + * The amount of time to suppress "power-off" from the power button after the device has + * woken due to a gesture (lifting the phone). Since users have learned to hit the power + * button immediately when lifting their device, it can cause the device to turn off if a + * gesture has just woken the device. This value tells us the milliseconds to wait after + * a gesture before "power-off" via power-button is functional again. A value of 0 is no + * delay, and reverts to the old behavior. + * + * @hide + */ + public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE = + "power_button_suppression_delay_after_gesture_wake"; } /** diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index f2aaeaddef97..e8b0d92c2fd2 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -53,7 +53,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_mobile_network_v2", "true"); DEFAULT_FLAGS.put("settings_network_and_internet_v2", "true"); - DEFAULT_FLAGS.put("settings_slice_injection", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java index 680e85fa2ba8..c1873d76f46f 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -44,6 +44,13 @@ public class SparseSetArray<T> { } /** + * Removes all mappings from this SparseSetArray. + */ + public void clear() { + mData.clear(); + } + + /** * @return whether a value exists at index n. */ public boolean contains(int n, T value) { diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 0051d01eec13..bbd44c8b85af 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -682,14 +682,6 @@ public final class AccessibilityInteractionController { // Handle this hidden action separately succeeded = handleClickableSpanActionUiThread( target, virtualDescendantId, arguments); - } else if (action == R.id.accessibilityActionOutsideTouch) { - // trigger ACTION_OUTSIDE to notify windows - final long now = SystemClock.uptimeMillis(); - MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE, - 0, 0, 0); - event.setSource(InputDevice.SOURCE_TOUCHSCREEN); - mViewRootImpl.dispatchInputEvent(event); - succeeded = true; } else { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { @@ -756,6 +748,33 @@ public final class AccessibilityInteractionController { } } + /** + * Notify outside touch event to the target window. + */ + public void notifyOutsideTouchClientThread() { + final Message message = mHandler.obtainMessage(); + message.what = PrivateHandler.MSG_NOTIFY_OUTSIDE_TOUCH; + + // Don't care about pid and tid because there's no interrogating client for this message. + scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS); + } + + private void notifyOutsideTouchUiThread() { + if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null + || mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) { + return; + } + final View root = mViewRootImpl.mView; + if (root != null && isShown(root)) { + // trigger ACTION_OUTSIDE to notify windows + final long now = SystemClock.uptimeMillis(); + final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_OUTSIDE, + 0, 0, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + mViewRootImpl.dispatchInputEvent(event); + } + } + private View findViewByAccessibilityId(int accessibilityId) { if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) { return mViewRootImpl.mView; @@ -1328,6 +1347,8 @@ public final class AccessibilityInteractionController { private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100; private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1; + private static final int MSG_NOTIFY_OUTSIDE_TOUCH = + FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 2; public PrivateHandler(Looper looper) { super(looper); @@ -1357,6 +1378,8 @@ public final class AccessibilityInteractionController { return "MSG_APP_PREPARATION_TIMEOUT"; case MSG_CLEAR_ACCESSIBILITY_FOCUS: return "MSG_CLEAR_ACCESSIBILITY_FOCUS"; + case MSG_NOTIFY_OUTSIDE_TOUCH: + return "MSG_NOTIFY_OUTSIDE_TOUCH"; default: throw new IllegalArgumentException("Unknown message type: " + type); } @@ -1396,6 +1419,9 @@ public final class AccessibilityInteractionController { case MSG_CLEAR_ACCESSIBILITY_FOCUS: { clearAccessibilityFocusUiThread(); } break; + case MSG_NOTIFY_OUTSIDE_TOUCH: { + notifyOutsideTouchUiThread(); + } break; default: throw new IllegalArgumentException("Unknown message type: " + type); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7ad118e760d8..865a656994b5 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2814,10 +2814,14 @@ public final class ViewRootImpl implements ViewParent, hasWindowFocus = mUpcomingWindowFocus; inTouchMode = mUpcomingInTouchMode; } - if (hasWindowFocus) { - mInsetsController.onWindowFocusGained(); - } else { - mInsetsController.onWindowFocusLost(); + if (sNewInsetsMode != NEW_INSETS_MODE_NONE) { + // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback + // config changes. + if (hasWindowFocus) { + mInsetsController.onWindowFocusGained(); + } else { + mInsetsController.onWindowFocusLost(); + } } if (mAdded) { @@ -8819,6 +8823,15 @@ public final class ViewRootImpl implements ViewParent, .clearAccessibilityFocusClientThread(); } } + + @Override + public void notifyOutsideTouch() { + ViewRootImpl viewRootImpl = mViewRootImpl.get(); + if (viewRootImpl != null && viewRootImpl.mView != null) { + viewRootImpl.getAccessibilityInteractionController() + .notifyOutsideTouchClientThread(); + } + } } private class SendWindowContentChangedAccessibilityEvent implements Runnable { diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index d12777fe908e..882e6fd8e328 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1015,7 +1015,7 @@ public final class AccessibilityManager { * @hide */ @SystemApi - public int getAccessibilityWindowId(IBinder windowToken) { + public int getAccessibilityWindowId(@Nullable IBinder windowToken) { if (windowToken == null) { return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 947ff056627e..deb0d2a3fd3d 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -57,4 +57,6 @@ oneway interface IAccessibilityInteractionConnection { int interrogatingPid, long interrogatingTid); void clearAccessibilityFocus(); + + void notifyOutsideTouch(); } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index a9770731dbc7..c2ad82fd5c5f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -294,6 +294,15 @@ public final class ContentCaptureManager { private MainContentCaptureSession mMainSession; /** @hide */ + public interface ContentCaptureClient { + /** + * Gets the component name of the client. + */ + @NonNull + ComponentName contentCaptureClientGetComponentName(); + } + + /** @hide */ public ContentCaptureManager(@NonNull Context context, @NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index be66de22cdcf..5930f8fd14d3 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -60,6 +60,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -479,6 +480,28 @@ public class ChooserActivity extends ResolverActivity { if (isSendAction(target)) { mResolverDrawerLayout.setOnScrollChangeListener(this::handleScroll); } + + final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header); + final float defaultElevation = chooserHeader.getElevation(); + final float chooserHeaderScrollElevation = + getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation); + + mAdapterView.setOnScrollListener(new AbsListView.OnScrollListener() { + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, + int totalItemCount) { + if (view.getChildCount() > 0) { + if (firstVisibleItem > 0 || view.getChildAt(0).getTop() < 0) { + chooserHeader.setElevation(chooserHeaderScrollElevation); + return; + } + } + + chooserHeader.setElevation(defaultElevation); + } + }); } if (DEBUG) { @@ -1018,12 +1041,9 @@ public class ChooserActivity extends ResolverActivity { int cat = 0; int value = which; int directTargetAlsoRanked = -1; + int numCallerProvided = 0; HashedStringCache.HashResult directTargetHashed = null; switch (mChooserListAdapter.getPositionTargetType(which)) { - case ChooserListAdapter.TARGET_CALLER: - cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET; - value -= mChooserListAdapter.getSelectableServiceTargetCount(); - break; case ChooserListAdapter.TARGET_SERVICE: cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET; // Log the package name + target name to answer the question if most users @@ -1039,13 +1059,14 @@ public class ChooserActivity extends ResolverActivity { directTargetAlsoRanked = getRankedPosition((SelectableTargetInfo) targetInfo); if (mCallerChooserTargets != null) { - value -= mCallerChooserTargets.length; + numCallerProvided = mCallerChooserTargets.length; } break; + case ChooserListAdapter.TARGET_CALLER: case ChooserListAdapter.TARGET_STANDARD: - cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET; - value -= mChooserListAdapter.getCallerTargetCount() - + mChooserListAdapter.getSelectableServiceTargetCount(); + cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET; + value -= mChooserListAdapter.getSelectableServiceTargetCount(); + numCallerProvided = mChooserListAdapter.getCallerTargetCount(); break; case ChooserListAdapter.TARGET_STANDARD_AZ: // A-Z targets are unranked standard targets; we use -1 to mark that they @@ -1066,8 +1087,9 @@ public class ChooserActivity extends ResolverActivity { targetLogMaker.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, directTargetAlsoRanked); } + targetLogMaker.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, + numCallerProvided); getMetricsLogger().write(targetLogMaker); - MetricsLogger.action(this, cat, value); } if (mIsSuccessfullySelected) { @@ -1680,7 +1702,10 @@ public class ChooserActivity extends ResolverActivity { final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { public Drawable getDisplayIcon() { - return getDrawable(R.drawable.resolver_icon_placeholder); + AnimatedVectorDrawable avd = (AnimatedVectorDrawable) + getDrawable(R.drawable.chooser_direct_share_icon_placeholder); + avd.start(); // Start animation after generation + return avd; } } @@ -1802,7 +1827,8 @@ public class ChooserActivity extends ResolverActivity { if (info == null) return null; // Now fetch app icon and raster with no badging even in work profile - Bitmap appIcon = makePresentationGetter(info).getIconBitmap(); + Bitmap appIcon = makePresentationGetter(info).getIconBitmap( + UserHandle.getUserHandleForUid(UserHandle.myUserId())); // Raster target drawable with appIcon as a badge SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 2849f57d6fc5..5e4918d475b8 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -551,11 +551,11 @@ public class ResolverActivity extends Activity { mAi.packageName); } - public Drawable getIcon() { - return new BitmapDrawable(mCtx.getResources(), getIconBitmap()); + public Drawable getIcon(UserHandle userHandle) { + return new BitmapDrawable(mCtx.getResources(), getIconBitmap(userHandle)); } - public Bitmap getIconBitmap() { + public Bitmap getIconBitmap(UserHandle userHandle) { Drawable dr = null; if (mHasSubstitutePermission) { dr = getIconSubstituteInternal(); @@ -576,7 +576,7 @@ public class ResolverActivity extends Activity { } SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx); - Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle()); + Bitmap icon = sif.createUserBadgedIconBitmap(dr, userHandle); sif.recycle(); return icon; @@ -699,7 +699,8 @@ public class ResolverActivity extends Activity { } Drawable loadIconForResolveInfo(ResolveInfo ri) { - return makePresentationGetter(ri).getIcon(); + // Load icons based on the current process. If in work profile icons should be badged. + return makePresentationGetter(ri).getIcon(Process.myUserHandle()); } @Override diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java index 2484109f8520..7a4e76fa8c17 100644 --- a/core/java/com/android/internal/app/SimpleIconFactory.java +++ b/core/java/com/android/internal/app/SimpleIconFactory.java @@ -43,7 +43,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.DrawableWrapper; -import android.os.Process; import android.os.UserHandle; import android.util.AttributeSet; import android.util.Pools.SynchronizedPool; @@ -161,6 +160,7 @@ public class SimpleIconFactory { /** * Creates bitmap using the source drawable and various parameters. * The bitmap is visually normalized with other icons and has enough spacing to add shadow. + * Note: this method has been modified from iconloaderlib to remove a profile diff check. * * @param icon source of the icon associated with a user that has no badge, * likely user 0 @@ -186,7 +186,7 @@ public class SimpleIconFactory { } final Bitmap result; - if (user != null && !Process.myUserHandle().equals(user)) { + if (user != null /* if modification from iconloaderlib */) { BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap); Drawable badged = mPm.getUserBadgedIcon(drawable, user); if (badged instanceof BitmapDrawable) { diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java index dfa59b7bd0ac..a0b2f94aea32 100644 --- a/core/java/com/android/internal/infra/GlobalWhitelistState.java +++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java @@ -35,11 +35,13 @@ import java.util.List; * * <p>This class is thread safe. */ +// TODO: add unit tests public class GlobalWhitelistState { // Uses full-name to avoid collision with service-provided mLock protected final Object mGlobalWhitelistStateLock = new Object(); + // TODO: should not be exposed directly @Nullable @GuardedBy("mGlobalWhitelistStateLock") protected SparseArray<WhitelistHelper> mWhitelisterHelpers; diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java index d7753db6b0f7..9d653bad4d00 100644 --- a/core/java/com/android/internal/infra/WhitelistHelper.java +++ b/core/java/com/android/internal/infra/WhitelistHelper.java @@ -98,9 +98,9 @@ public final class WhitelistHelper { @Nullable List<ComponentName> components) { final ArraySet<String> packageNamesSet = packageNames == null ? null : new ArraySet<>(packageNames); - final ArraySet<ComponentName> componentssSet = components == null ? null + final ArraySet<ComponentName> componentsSet = components == null ? null : new ArraySet<>(components); - setWhitelist(packageNamesSet, componentssSet); + setWhitelist(packageNamesSet, componentsSet); } /** @@ -170,7 +170,7 @@ public final class WhitelistHelper { pw.print("["); pw.print(components.valueAt(0)); for (int j = 1; j < components.size(); j++) { - pw.print(", "); pw.print(components.valueAt(i)); + pw.print(", "); pw.print(components.valueAt(j)); } pw.println("]"); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1fc76351254b..72f6c122fe3a 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -609,9 +609,7 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_RADIO = 0x04; int UPDATE_BT = 0x08; int UPDATE_RPM = 0x10; // 16 - int UPDATE_RAIL = 0x20; // 32 - int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM - | UPDATE_RAIL; + int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM; Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 61799183e595..dcf8d285eca9 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -19,10 +19,10 @@ package com.android.internal.os; import static android.os.Process.*; import android.annotation.UnsupportedAppUsage; -import android.os.FileUtils; import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; +import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Slog; @@ -247,6 +247,7 @@ public class ProcessCpuTracker { pid = _pid; if (parentPid < 0) { final File procDir = new File("/proc", Integer.toString(pid)); + uid = getUid(procDir.toString()); statFile = new File(procDir, "stat").toString(); cmdlineFile = new File(procDir, "cmdline").toString(); threadsDir = (new File(procDir, "task")).toString(); @@ -262,13 +263,22 @@ public class ProcessCpuTracker { parentPid)); final File taskDir = new File( new File(procDir, "task"), Integer.toString(pid)); + uid = getUid(taskDir.toString()); statFile = new File(taskDir, "stat").toString(); cmdlineFile = null; threadsDir = null; threadStats = null; workingThreads = null; } - uid = FileUtils.getUid(statFile.toString()); + } + + private static int getUid(String path) { + try { + return Os.stat(path).st_uid; + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to stat(" + path + "): " + e); + return -1; + } } } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 8f8e4d8dc108..f22b6cd3d601 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -76,6 +76,7 @@ interface IStatusBarService in int notificationLocation, boolean modifiedBeforeSending); void onNotificationSettingsViewed(String key); void setSystemUiVisibility(int displayId, int vis, int mask, String cause); + void onNotificationBubbleChanged(String key, boolean isBubble); void onGlobalActionsShown(); void onGlobalActionsHidden(); diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 61d5031e16ed..b49998157ad2 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -256,6 +256,10 @@ void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int ssize_t offset; size_t size; sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); + if (heap == NULL) { + ALOGV("copyAndPost: skipping null memory callback!"); + return; + } ALOGV("copyAndPost: off=%zd, size=%zu", offset, size); uint8_t *heapBase = (uint8_t*)heap->base(); diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp index 7052ed613591..4fad239b5743 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -1010,9 +1010,10 @@ static inline bool unDistortWithinPreCorrArray( const float cx, const float cy, const float f, const int preCorrW, const int preCorrH, const int xMin, const int yMin) { undistort(x, y, distortion, cx, cy, f); - int xMax = xMin + preCorrW - 1; - int yMax = yMin + preCorrH - 1; - if (x < xMin || y < yMin || x > xMax || y > yMax) { + // xMin and yMin are inclusive, and xMax and yMax are exclusive. + int xMax = xMin + preCorrW; + int yMax = yMin + preCorrH; + if (x < xMin || y < yMin || x >= xMax || y >= yMax) { return false; } return true; @@ -1976,7 +1977,6 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image if (entry3.count == 5) { gotDistortion = true; - // Scale the distortion coefficients to create a zoom in warpped image so that all // pixels are drawn within input image. for (size_t i = 0; i < entry3.count; i++) { @@ -1995,8 +1995,8 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image preXMin, preYMin); } - float m_x = std::fmaxf(preWidth-1 - cx, cx); - float m_y = std::fmaxf(preHeight-1 - cy, cy); + float m_x = std::fmaxf(preWidth - cx, cx); + float m_y = std::fmaxf(preHeight - cy, cy); float m_sq = m_x*m_x + m_y*m_y; float m = sqrtf(m_sq); // distance to farthest corner from optical center float f_sq = f * f; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d3f9196ce763..2b471fec9c8f 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -163,7 +163,7 @@ static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { } // Generic idmap parameters - const char* argv[9]; + const char* argv[10]; int argc = 0; struct stat st; @@ -203,6 +203,10 @@ static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { argv[argc++] = AssetManager::ODM_OVERLAY_DIR; } + if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OEM_OVERLAY_DIR; + } + // Finally, invoke idmap (if any overlay directory exists) if (argc > 5) { execv(AssetManager::IDMAP_BIN, (char* const*)argv); @@ -241,6 +245,10 @@ static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* input_dirs.push_back(AssetManager::ODM_OVERLAY_DIR); } + if (stat(AssetManager::OEM_OVERLAY_DIR, &st) == 0) { + input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR); + } + if (input_dirs.empty()) { LOG(WARNING) << "no directories for idmap2 to scan"; return env->NewObjectArray(0, g_stringClass, nullptr); diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index a9002945ff91..ccadc7d7c22a 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -212,6 +212,10 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, return 0; } + if (convertPixelFormat(ANativeWindow_getFormat(surface.get())) == kUnknown_SkColorType) { + native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888); + } + Rect dirtyRect(Rect::EMPTY_RECT); Rect* dirtyRectPtr = NULL; diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 77ebd0290d33..fa5f931470b0 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -104,6 +104,8 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { static const char* kProductServicesOverlayDir = "/product_services/overlay"; static const char* kSystemOdmOverlayDir = "/system/odm/overlay"; static const char* kOdmOverlayDir = "/odm/overlay"; + static const char* kSystemOemOverlayDir = "/system/oem/overlay"; + static const char* kOemOverlayDir = "/oem/overlay"; static const char* kApkSuffix = ".apk"; if ((android::base::StartsWith(path, kOverlayDir) @@ -114,7 +116,9 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { || android::base::StartsWith(path, kSystemProductServicesOverlayDir) || android::base::StartsWith(path, kProductServicesOverlayDir) || android::base::StartsWith(path, kSystemOdmOverlayDir) - || android::base::StartsWith(path, kOdmOverlayDir)) + || android::base::StartsWith(path, kOdmOverlayDir) + || android::base::StartsWith(path, kSystemOemOverlayDir) + || android::base::StartsWith(path, kOemOverlayDir)) && android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) { return true; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index ace88f503e72..1ff7418a3ef8 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -665,6 +665,15 @@ enum Action { //ACTION: Log result for each card's eligibility check ACTION_CONTEXTUAL_CARD_ELIGIBILITY = 1686; + + // ACTION: Share a Wi-Fi network by generating a QR code + ACTION_SETTINGS_SHARE_WIFI_QR_CODE = 1710; + + // ACTION: Connect to a Wi-Fi network by scanning a QR code + ACTION_SETTINGS_ENROLL_WIFI_QR_CODE = 1711; + + // ACTION: Share Wi-Fi hotspot by generating a QR code + ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712; } /** @@ -2336,4 +2345,13 @@ enum PageId { // CATEGORY: SETTINGS // OS: Q ACTION_DISPLAY_WHITE_BALANCE_SETTING_CHANGED = 1703; + + // OPEN: Settings > Pick SIM dialog + DIALOG_SIM_LIST = 1707; + + // OPEN: Settings > Pick SIM (that supports calling) dialog + DIALOG_CALL_SIM_LIST = 1708; + + // OPEN: Settings > Pick preferred SIM dialog + DIALOG_PREFERRED_SIM_PICKER = 1709; } diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto index 2de538d358c4..014d71c6fcf8 100644 --- a/core/proto/android/content/intent.proto +++ b/core/proto/android/content/intent.proto @@ -23,7 +23,7 @@ import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/os/patternmatcher.proto"; import "frameworks/base/core/proto/android/privacy.proto"; -// Next Tag: 13 +// Next Tag: 14 message IntentProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -62,6 +62,7 @@ message IntentProto { // UserHandle value (similar to user_id in other protos). optional int32 content_user_hint = 11; optional string selector = 12; + optional string identifier = 13 [ (.android.privacy).dest = DEST_EXPLICIT ]; } // Next Tag: 11 diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 1e0b0d84e04d..dc2e6d5d2d41 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -478,6 +478,54 @@ message StateControllerProto { } repeated TrackedJob tracked_jobs = 4; + message ExecutionStats { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional JobStatusDumpProto.Bucket standby_bucket = 1; + + // The time after which this record should be considered invalid (out of date), in the + // elapsed realtime timebase. + optional int64 expiration_time_elapsed = 2; + optional int64 window_size_ms = 3; + + /** The total amount of time the app ran in its respective bucket window size. */ + optional int64 execution_time_in_window_ms = 4; + optional int32 bg_job_count_in_window = 5; + + /** + * The total amount of time the app ran in the last + * {@link QuotaController#MAX_PERIOD_MS}. + */ + optional int64 execution_time_in_max_period_ms = 6; + optional int32 bg_job_count_in_max_period = 7; + + /** + * The time after which the sum of all the app's sessions plus + * ConstantsProto.QuotaController.in_quota_buffer_ms equals the quota. This is only + * valid if + * execution_time_in_window_ms >= + * ConstantsProto.QuotaController.allowed_time_per_period_ms + * or + * execution_time_in_max_period_ms >= + * ConstantsProto.QuotaController.max_execution_time_ms. + */ + optional int64 quota_cutoff_time_elapsed = 8; + + /** + * The time after which job_count_in_allowed_time should be considered invalid, in the + * elapsed realtime timebase. + */ + optional int64 job_count_expiration_time_elapsed = 9; + + /** + * The number of jobs that ran in at least the last + * ConstantsProto.QuotaController.allowed_time_per_period_ms. + * It may contain a few stale entries since cleanup won't happen exactly every + * ConstantsProto.QuotaController.allowed_time_per_period_ms. + */ + optional int32 job_count_in_allowed_time = 10; + } + message Package { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -517,6 +565,8 @@ message StateControllerProto { optional Timer timer = 2; repeated TimingSession saved_sessions = 3; + + repeated ExecutionStats execution_stats = 4; } repeated PackageStats package_stats = 5; } diff --git a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml new file mode 100644 index 000000000000..838cb49ca79e --- /dev/null +++ b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="36dp" + android:height="36dp" + android:viewportHeight="64" + android:viewportWidth="64"> + + <group android:name="background"> + <path android:pathData="M0,0 L 64,0 64,64 0,64 z" + android:fillColor="@color/chooser_gradient_background"/> + </group> + + <!-- Gradient starts offscreen so it is not visible in the first frame before start --> + <group android:name="gradient" android:translateX="-128"> + <path + android:pathData="M0,0 L 128,0 128,128 0,128 z"> + <aapt:attr name="android:fillColor"> + <gradient + android:type="linear" + android:startX="0" + android:endX="128" + android:startY="0" + android:endY="0"> + <item + android:color="@android:color/transparent" + android:offset="0.0" /> + <item + android:color="@color/chooser_gradient_highlight" + android:offset="0.5" /> + <item + android:color="@android:color/transparent" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + </group> + + <!-- Use a foregroud with a cutout shape matching direct share inset for appx applied + shadow. Using clip-path is a more elegant solution but leaves awful jaggies around + the path's shape. --> + <group android:name="cover"> + <path android:fillColor="?attr/colorBackgroundFloating" + android:pathData="M0,0 L64,0 L64,64 L0,64 L0,0 Z M59.0587325,42.453601 C60.3124932,39.2104785 61,35.6855272 61,32 C61,15.9837423 48.0162577,3 32,3 C15.9837423,3 3,15.9837423 3,32 C3,48.0162577 15.9837423,61 32,61 C35.6855272,61 39.2104785,60.3124932 42.453601,59.0587325 C44.3362195,60.2864794 46.5847839,61 49,61 C55.627417,61 61,55.627417 61,49 C61,46.5847839 60.2864794,44.3362195 59.0587325,42.453601 Z"/> + </group> + </vector> + </aapt:attr> + + <!-- This AVD uses special properties so that once started it will loop infinitely with no + need for callbacks to restart. --> + <target android:name="gradient"> + <aapt:attr name="android:animation"> + <objectAnimator + android:duration="1700" + android:pathData="M -128,0 L 192,0" + android:propertyXName="translateX" + android:repeatMode="restart" + android:repeatCount="infinite" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </aapt:attr> + </target> +</animated-vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml index 940da946cbb8..ee58738b75d0 100644 --- a/core/res/res/drawable/ic_content_copy_gm2.xml +++ b/core/res/res/drawable/ic_content_copy_gm2.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> <path - android:fillColor="#F999" + android:fillColor="?android:attr/textColorSecondary" android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/> </vector> diff --git a/core/res/res/drawable/resolver_icon_placeholder.xml b/core/res/res/drawable/resolver_icon_placeholder.xml index 049cfeecc4c6..7236fbe8a655 100644 --- a/core/res/res/drawable/resolver_icon_placeholder.xml +++ b/core/res/res/drawable/resolver_icon_placeholder.xml @@ -14,6 +14,6 @@ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> - <solid android:color="#10000000"/> + <solid android:color="@color/chooser_gradient_background"/> <size android:width="36dp" android:height="36dp"/> </shape>
\ No newline at end of file diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 138e24e36753..8727f4c9e0ef 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -25,9 +25,11 @@ android:id="@id/contentPanel"> <RelativeLayout + android:id="@+id/chooser_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" + android:elevation="1dp" android:background="@drawable/bottomsheet_background"> <ImageView @@ -61,11 +63,11 @@ android:layout_height="match_parent" android:id="@+id/resolver_list" android:clipToPadding="false" - android:scrollbarStyle="outsideOverlay" android:background="?attr/colorBackgroundFloating" + android:scrollbars="none" android:listSelector="@color/transparent" android:divider="@null" - android:scrollIndicators="top" + android:elevation="1dp" android:nestedScrollingEnabled="true"/> <TextView android:id="@+id/empty" @@ -76,6 +78,7 @@ android:text="@string/noApplications" android:padding="@dimen/chooser_edge_margin_normal" android:gravity="center" + android:elevation="1dp" android:visibility="gone"/> </com.android.internal.widget.ResolverDrawerLayout> diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 6aca49b673a3..c5e72f012a21 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -30,4 +30,6 @@ <color name="notification_material_background_color">@color/black</color> <color name="chooser_row_divider">@color/list_divider_color_dark</color> + <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color> + <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index bbc784a10db3..26a9f570a6d2 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -346,13 +346,21 @@ By setting this value on two or more packages, each of these packages will be given a single shared user ID, so they can for example run in the same process. Note that for them to actually get the same - user ID, they must also be signed with the same signature. --> + user ID, they must also be signed with the same signature. + @deprecated Shared user id's cause non-deterministic behaviour within the + package manager. As such, it's use is discouraged, deprecated, and will + be removed altogether in a future version of Android. Instead, proper + communication mechanisms such as services and providers should be used + to facilitate interoperability between shared components. --> <attr name="sharedUserId" format="string" /> <!-- Specify a label for the shared user UID of this package. This is only used if you have also used android:sharedUserId. This must be a reference to a string resource; it can not be an explicit - string. --> + string. + @deprecated There is no replacement for this attribute. + {@link android.R.attr#sharedUserId} has been deprecated making + this attribute unnecessary. --> <attr name="sharedUserLabel" format="reference" /> <!-- Internal version code. This is the number used to determine whether diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index ef26cd74cde0..048f341ffe32 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -216,4 +216,6 @@ <color name="default_magnifier_color_overlay">#00FFFFFF</color> <color name="chooser_row_divider">@color/list_divider_color_light</color> + <color name="chooser_gradient_background">@color/loading_gradient_background_color_light</color> + <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_light</color> </resources> diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 5af3c2af01db..112f98e5ef35 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -48,4 +48,8 @@ <color name="list_divider_color_light">#ffdadce0</color> <color name="list_divider_color_dark">#85ffffff</color> + <color name="loading_gradient_background_color_dark">#2D3033</color> + <color name="loading_gradient_background_color_light">#DADCE0</color> + <color name="loading_gradient_highlight_color_dark">#3C4043</color> + <color name="loading_gradient_highlight_color_light">#F1F3F4</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b3e94e39f30e..21a8f4c7ff89 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -572,6 +572,15 @@ <!-- Integer size limit, in KB, for a single WifiLogger ringbuffer, in verbose logging mode --> <integer translatable="false" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb">1024</integer> + <!-- Array indicating wifi fatal firmware alert error code list from driver --> + <integer-array translatable="false" name="config_wifi_fatal_firmware_alert_error_code_list"> + <!-- Example: + <item>0</item> + <item>1</item> + <item>2</item> + --> + </integer-array> + <!-- Boolean indicating whether or not wifi should turn off when emergency call is made --> <bool translatable="false" name="config_wifi_turn_off_during_emergency_call">false</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index b81db158082c..d6ec414533c3 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -51,18 +51,23 @@ that just start below the notch. --> <dimen name="display_cutout_touchable_region_size">12dp</dimen> - <!-- EXPERIMENT BEGIN --> <!-- Height of the bottom navigation bar frame; this is different than navigation_bar_height where that is the height reported to all the other windows to resize themselves around the navigation bar window but navigation_bar_frame_height is reported to SystemUI navigation bar view's window --> <dimen name="navigation_bar_frame_height">@dimen/navigation_bar_height</dimen> + <!-- Height of the bottom navigation bar frame in landscape --> + <dimen name="navigation_bar_frame_height_landscape">@dimen/navigation_bar_frame_height</dimen> <!-- Width of the left/right navigation bar frame; this is different than navigation_bar_width where that is the width reported to all the other windows to resize themselves around the navigation bar window but navigation_bar_frame_width is reported to SystemUI navigation bar view's window --> <dimen name="navigation_bar_frame_width">@dimen/navigation_bar_width</dimen> - <!-- EXPERIMENT END--> + + <!-- The height of the navigation gesture area; if the size is larger than the navigation bar + frame width/height, then the difference is the spacing from the navigation bar window to + the area that detects gestures. --> + <dimen name="navigation_bar_gesture_height">@dimen/navigation_bar_frame_height</dimen> <!-- Height of the bottom navigation / system bar in car mode. --> <dimen name="navigation_bar_height_car_mode">96dp</dimen> @@ -729,5 +734,6 @@ <dimen name="resolver_icon_size">42dp</dimen> <dimen name="resolver_badge_size">18dp</dimen> <dimen name="chooser_target_width">90dp</dimen> + <dimen name="chooser_header_scroll_elevation">4dp</dimen> <dimen name="chooser_max_collapsed_height">288dp</dimen> </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index ce7995a93f7f..2b0c86b49577 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -185,9 +185,6 @@ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_HIDE_TOOLTIP}. --> <item type="id" name="accessibilityActionHideTooltip" /> - <!-- Accessibility action to notify a window there is an outside touch. --> - <item type="id" name="accessibilityActionOutsideTouch" /> - <!-- A tag used to save the view added to a transition overlay --> <item type="id" name="transition_overlay_view_tag" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e19e08c4b5a9..470c9ed472dc 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -245,7 +245,6 @@ <java-symbol type="id" name="selection_end_handle" /> <java-symbol type="id" name="insertion_handle" /> <java-symbol type="id" name="accessibilityActionClickOnClickableSpan" /> - <java-symbol type="id" name="accessibilityActionOutsideTouch" /> <java-symbol type="id" name="camera" /> <java-symbol type="id" name="mic" /> <java-symbol type="id" name="overlay" /> @@ -381,6 +380,7 @@ <java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" /> <java-symbol type="integer" name="config_wifi_logger_ring_buffer_default_size_limit_kb" /> <java-symbol type="integer" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb" /> + <java-symbol type="array" name="config_wifi_fatal_firmware_alert_error_code_list" /> <java-symbol type="bool" name="config_wifi_turn_off_during_emergency_call" /> <java-symbol type="bool" name="config_supportMicNearUltrasound" /> <java-symbol type="bool" name="config_supportSpeakerNearUltrasound" /> @@ -1746,6 +1746,8 @@ <java-symbol type="dimen" name="navigation_bar_height_landscape" /> <java-symbol type="dimen" name="navigation_bar_width" /> <java-symbol type="dimen" name="navigation_bar_frame_height" /> + <java-symbol type="dimen" name="navigation_bar_frame_height_landscape" /> + <java-symbol type="dimen" name="navigation_bar_gesture_height" /> <java-symbol type="dimen" name="navigation_bar_frame_width" /> <java-symbol type="dimen" name="navigation_bar_height_car_mode" /> <java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" /> @@ -2786,6 +2788,8 @@ <java-symbol type="dimen" name="chooser_preview_image_border"/> <java-symbol type="dimen" name="chooser_max_collapsed_height" /> <java-symbol type="layout" name="chooser_grid" /> + <java-symbol type="id" name="chooser_header" /> + <java-symbol type="dimen" name="chooser_header_scroll_elevation" /> <java-symbol type="layout" name="chooser_grid_preview_text" /> <java-symbol type="layout" name="chooser_grid_preview_image" /> <java-symbol type="layout" name="chooser_grid_preview_file" /> @@ -3762,4 +3766,8 @@ <java-symbol type="drawable" name="perm_group_camera" /> <java-symbol type="drawable" name="perm_group_location" /> <java-symbol type="drawable" name="perm_group_microphone" /> + + <java-symbol type="drawable" name="chooser_direct_share_icon_placeholder" /> + <java-symbol type="color" name="chooser_gradient_background" /> + <java-symbol type="color" name="chooser_gradient_highlight" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 03fb1fc72b45..0ed9860c9439 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -802,6 +802,8 @@ easier. <item name="textAppearanceListItemSmall">@style/TextAppearance.DeviceDefault.ListItem</item> <item name="textAppearanceListItemSecondary">@style/TextAppearance.DeviceDefault.ListItemSecondary</item> + <item name="backgroundDimAmount">0.7</item> + <!-- Button styles --> <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item> <item name="buttonStyle">@style/Widget.DeviceDefault.Light.Button</item> diff --git a/core/tests/coretests/res/values/overlayable_icons_test.xml b/core/tests/coretests/res/values/overlayable_icons_test.xml new file mode 100644 index 000000000000..9bc46de3505a --- /dev/null +++ b/core/tests/coretests/res/values/overlayable_icons_test.xml @@ -0,0 +1,67 @@ +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- overlayable_icons references all of the drawables in this package + that are being overlayed by resource overlays. If you remove/rename + any of these resources, you must also change the resource overlay icons.--> + <array name="overlayable_icons"> + <item>@*android:drawable/ic_audio_alarm</item> + <item>@*android:drawable/ic_audio_alarm_mute</item> + <item>@*android:drawable/ic_bluetooth_share_icon</item> + <item>@*android:drawable/ic_bt_headphones_a2dp</item> + <item>@*android:drawable/ic_bt_headset_hfp</item> + <item>@*android:drawable/ic_bt_hearing_aid</item> + <item>@*android:drawable/ic_bt_laptop</item> + <item>@*android:drawable/ic_bt_network_pan</item> + <item>@*android:drawable/ic_bt_pointing_hid</item> + <item>@*android:drawable/ic_expand_more</item> + <item>@*android:drawable/ic_file_copy</item> + <item>@*android:drawable/ic_info_outline_24</item> + <item>@*android:drawable/ic_lock</item> + <item>@*android:drawable/ic_lock_bugreport</item> + <item>@*android:drawable/ic_lock_open</item> + <item>@*android:drawable/ic_lock_power_off</item> + <item>@*android:drawable/ic_lockscreen_ime</item> + <item>@*android:drawable/ic_mode_edit</item> + <item>@*android:drawable/ic_phone</item> + <item>@*android:drawable/ic_qs_airplane</item> + <item>@*android:drawable/ic_qs_auto_rotate</item> + <item>@*android:drawable/ic_qs_bluetooth</item> + <item>@*android:drawable/ic_qs_dnd</item> + <item>@*android:drawable/ic_qs_flashlight</item> + <item>@*android:drawable/ic_qs_night_display_on</item> + <item>@*android:drawable/ic_restart</item> + <item>@*android:drawable/ic_screenshot</item> + <item>@*android:drawable/ic_settings_bluetooth</item> + <item>@*android:drawable/ic_signal_location</item> + <item>@*android:drawable/ic_wifi_signal_0</item> + <item>@*android:drawable/ic_wifi_signal_1</item> + <item>@*android:drawable/ic_wifi_signal_2</item> + <item>@*android:drawable/ic_wifi_signal_3</item> + <item>@*android:drawable/ic_wifi_signal_4</item> + <item>@*android:drawable/perm_group_activity_recognition</item> + <item>@*android:drawable/perm_group_call_log</item> + <item>@*android:drawable/perm_group_camera</item> + <item>@*android:drawable/perm_group_contacts</item> + <item>@*android:drawable/perm_group_location</item> + <item>@*android:drawable/perm_group_microphone</item> + <item>@*android:drawable/perm_group_phone_calls</item> + <item>@*android:drawable/perm_group_sensors</item> + <item>@*android:drawable/perm_group_sms</item> + <item>@*android:drawable/perm_group_storage</item> + <item>@*android:drawable/perm_group_visual</item> + </array> +</resources> diff --git a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java new file mode 100644 index 000000000000..c6f4fa27dc83 --- /dev/null +++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 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.content; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArraySet; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit test for {@link ContentCaptureOptions}. + * + * <p>To run it: + * {@code atest FrameworksCoreTests:android.content.ContentCaptureOptionsTest} + */ +@RunWith(MockitoJUnitRunner.class) +public class ContentCaptureOptionsTest { + + private final ComponentName mContextComponent = new ComponentName("marco", "polo"); + private final ComponentName mComp1 = new ComponentName("comp", "one"); + private final ComponentName mComp2 = new ComponentName("two", "comp"); + + @Mock private Context mContext; + @Mock private ContentCaptureClient mClient; + + @Before + public void setExpectation() { + when(mClient.contentCaptureClientGetComponentName()).thenReturn(mContextComponent); + when(mContext.getContentCaptureClient()).thenReturn(mClient); + } + + @Test + public void testIsWhitelisted_nullWhitelistedComponents() { + ContentCaptureOptions options = new ContentCaptureOptions(null); + assertThat(options.isWhitelisted(mContext)).isTrue(); + } + + @Test + public void testIsWhitelisted_emptyWhitelistedComponents() { + ContentCaptureOptions options = new ContentCaptureOptions(toSet((ComponentName) null)); + assertThat(options.isWhitelisted(mContext)).isFalse(); + } + + @Test + public void testIsWhitelisted_notWhitelisted() { + ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mComp2)); + assertThat(options.isWhitelisted(mContext)).isFalse(); + } + + @Test + public void testIsWhitelisted_whitelisted() { + ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mContextComponent)); + assertThat(options.isWhitelisted(mContext)).isTrue(); + } + + @Test + public void testIsWhitelisted_invalidContext() { + ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent)); + Context invalidContext = mock(Context.class); // has no client + assertThat(options.isWhitelisted(invalidContext)).isFalse(); + } + + @Test + public void testIsWhitelisted_clientWithNullComponentName() { + ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent)); + ContentCaptureClient client = mock(ContentCaptureClient.class); + Context context = mock(Context.class); + when(context.getContentCaptureClient()).thenReturn(client); + + assertThat(options.isWhitelisted(context)).isFalse(); + } + + @NonNull + private ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) { + ArraySet<ComponentName> set = new ArraySet<>(); + if (comps != null) { + for (int i = 0; i < comps.length; i++) { + set.add(comps[i]); + } + } + return set; + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index a853121bac4e..f4d3c8163e9c 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -392,6 +392,7 @@ public class SettingsBackupTest { Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, Settings.Global.POLICY_CONTROL, + Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE, Settings.Global.POWER_MANAGER_CONSTANTS, Settings.Global.PREFERRED_NETWORK_MODE, Settings.Global.PRIVATE_DNS_DEFAULT_MODE, diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index e4a93e740c4c..dbbe1b4ca574 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -41,6 +41,7 @@ applications that come with the platform <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> </privapp-permissions> <privapp-permissions package="com.android.defcontainer"> diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index c8b361bbff2f..6d20ec32cdc4 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -596,8 +596,8 @@ public class Typeface { final Font font = mFontBuilder.build(); final String key = mAssetManager == null ? null : createAssetUid( mAssetManager, mPath, font.getTtcIndex(), font.getAxes(), - font.getStyle().getWeight(), font.getStyle().getSlant(), - mFallbackFamilyName); + mWeight, mItalic, + mFallbackFamilyName == null ? DEFAULT_FAMILY : mFallbackFamilyName); if (key != null) { // Dynamic cache lookup is only for assets. synchronized (sDynamicCacheLock) { diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 21609d30e92c..4755cb866310 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -76,6 +76,7 @@ const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay"; const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay"; const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay"; const char* AssetManager::ODM_OVERLAY_DIR = "/odm/overlay"; +const char* AssetManager::OEM_OVERLAY_DIR = "/oem/overlay"; const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme"; const char* AssetManager::TARGET_PACKAGE_NAME = "android"; const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk"; diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index a015eabc200c..66fba26b7289 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -63,6 +63,7 @@ public: static const char* PRODUCT_OVERLAY_DIR; static const char* PRODUCT_SERVICES_OVERLAY_DIR; static const char* ODM_OVERLAY_DIR; + static const char* OEM_OVERLAY_DIR; /* * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay * APKs in VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index cf5d7ce3f738..0a9d965d0444 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -45,12 +45,12 @@ static constexpr android::DisplayInfo sDummyDisplay{ 1920, // viewportH }; -const DeviceInfo* DeviceInfo::get() { +DeviceInfo* DeviceInfo::get() { static DeviceInfo sDeviceInfo; return &sDeviceInfo; } -DisplayInfo QueryDisplayInfo() { +static DisplayInfo QueryDisplayInfo() { if (Properties::isolatedProcess) { return sDummyDisplay; } @@ -65,6 +65,27 @@ DisplayInfo QueryDisplayInfo() { return displayInfo; } +static float QueryMaxRefreshRate() { + if (Properties::isolatedProcess) { + return sDummyDisplay.fps; + } + + const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); + LOG_ALWAYS_FATAL_IF(token == nullptr, + "Failed to get display info because internal display is disconnected"); + + Vector<DisplayInfo> configs; + configs.reserve(10); + status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs); + LOG_ALWAYS_FATAL_IF(status, "Failed to getDisplayConfigs, error %d", status); + LOG_ALWAYS_FATAL_IF(configs.size() == 0, "getDisplayConfigs returned 0 configs?"); + float max = 0.0f; + for (auto& info : configs) { + max = std::max(max, info.fps); + } + return max; +} + static void queryWideColorGamutPreference(sk_sp<SkColorSpace>* colorSpace, SkColorType* colorType) { if (Properties::isolatedProcess) { *colorSpace = SkColorSpace::MakeSRGB(); @@ -103,7 +124,7 @@ static void queryWideColorGamutPreference(sk_sp<SkColorSpace>* colorSpace, SkCol } } -DeviceInfo::DeviceInfo() { +DeviceInfo::DeviceInfo() : mMaxRefreshRate(QueryMaxRefreshRate()) { #if HWUI_NULL_GPU mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; #else @@ -119,7 +140,11 @@ int DeviceInfo::maxTextureSize() const { } void DeviceInfo::setMaxTextureSize(int maxTextureSize) { - const_cast<DeviceInfo*>(DeviceInfo::get())->mMaxTextureSize = maxTextureSize; + DeviceInfo::get()->mMaxTextureSize = maxTextureSize; +} + +void DeviceInfo::onDisplayConfigChanged() { + mDisplayInfo = QueryDisplayInfo(); } } /* namespace uirenderer */ diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 2bab5d3596cf..0e3f11960ddc 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -32,7 +32,7 @@ class DeviceInfo { PREVENT_COPY_AND_ASSIGN(DeviceInfo); public: - static const DeviceInfo* get(); + static DeviceInfo* get(); // this value is only valid after the GPU has been initialized and there is a valid graphics // context or if you are using the HWUI_NULL_GPU @@ -40,6 +40,9 @@ public: const DisplayInfo& displayInfo() const { return mDisplayInfo; } sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; } SkColorType getWideColorType() const { return mWideColorType; } + float getMaxRefreshRate() const { return mMaxRefreshRate; } + + void onDisplayConfigChanged(); private: friend class renderthread::RenderThread; @@ -51,6 +54,7 @@ private: DisplayInfo mDisplayInfo; sk_sp<SkColorSpace> mWideColorSpace; SkColorType mWideColorType; + const float mMaxRefreshRate; }; } /* namespace uirenderer */ diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 99988542d619..19f509ce8fd7 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -67,7 +67,7 @@ bool Properties::debuggingEnabled = false; bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; -uint32_t Properties::defaultRenderAhead = 0; +int Properties::defaultRenderAhead = -1; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = { @@ -130,9 +130,8 @@ bool Properties::load() { enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true); - defaultRenderAhead = - std::max(0u, std::min(2u, static_cast<uint32_t>(property_get_int( - PROPERTY_RENDERAHEAD, render_ahead().value_or(0))))); + defaultRenderAhead = std::max(-1, std::min(2, property_get_int(PROPERTY_RENDERAHEAD, + render_ahead().value_or(0)))); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 3105e58362ec..3e91c63fcbde 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -253,7 +253,7 @@ public: ANDROID_API static int contextPriority; - static uint32_t defaultRenderAhead; + static int defaultRenderAhead; private: static ProfileType sProfileType; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 7cb241d22668..1b638c12ac7b 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -126,6 +126,10 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes); break; } + + // We must sync the cpu to make sure deletions of resources still queued up on the GPU actually + // happen. + mGrContext->flush(kSyncCpu_GrFlushFlag, 0, nullptr); } void CacheManager::trimStaleResources() { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 2957b143a343..f326ce8d23e9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -105,13 +105,13 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mGenerationID(0) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) - , mJankTracker(&thread.globalProfileData(), thread.mainDisplayInfo()) + , mJankTracker(&thread.globalProfileData(), DeviceInfo::get()->displayInfo()) , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos()) , mContentDrawBounds(0, 0, 0, 0) , mRenderPipeline(std::move(renderPipeline)) { rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); - mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); + mProfiler.setDensity(DeviceInfo::get()->displayInfo().density); setRenderAheadDepth(Properties::defaultRenderAhead); } @@ -153,16 +153,23 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { mNativeSurface = nullptr; } + if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) { + mFixedRenderAhead = false; + mRenderAheadCapacity = 1; + } else { + mFixedRenderAhead = true; + mRenderAheadCapacity = mRenderAheadDepth; + } + ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode, - mRenderAheadDepth); + mRenderAheadCapacity); mFrameNumber = -1; if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); - applyRenderAheadSettings(); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; @@ -403,6 +410,23 @@ void CanvasContext::notifyFramePending() { mRenderThread.pushBackFrameCallback(this); } +void CanvasContext::setPresentTime() { + int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO; + int renderAhead = 0; + const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos(); + if (mFixedRenderAhead) { + renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity); + } else if (frameIntervalNanos < 15_ms) { + renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity)); + } + + if (renderAhead) { + presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + + (frameIntervalNanos * (renderAhead + 1)); + } + native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); +} + void CanvasContext::draw() { SkRect dirty; mDamageAccumulator.finish(&dirty); @@ -415,14 +439,9 @@ void CanvasContext::draw() { mCurrentFrameInfo->markIssueDrawCommandsStart(); Frame frame = mRenderPipeline->getFrame(); + setPresentTime(); SkRect windowDirty = computeDirtyRect(frame, &dirty); - if (mRenderAheadDepth) { - auto presentTime = - mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + - (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1)); - native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); - } bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, @@ -656,18 +675,12 @@ bool CanvasContext::surfaceRequiresRedraw() { return width == mLastFrameWidth && height == mLastFrameHeight; } -void CanvasContext::applyRenderAheadSettings() { - if (mNativeSurface && !mRenderAheadDepth) { - native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO); - } -} - -void CanvasContext::setRenderAheadDepth(uint32_t renderAhead) { - if (renderAhead > 2 || renderAhead == mRenderAheadDepth || mNativeSurface) { +void CanvasContext::setRenderAheadDepth(int renderAhead) { + if (renderAhead > 2 || renderAhead < 0 || mNativeSurface) { return; } - mRenderAheadDepth = renderAhead; - applyRenderAheadSettings(); + mFixedRenderAhead = true; + mRenderAheadDepth = static_cast<uint32_t>(renderAhead); } SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 912b1257de7b..f9de00288e65 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -204,7 +204,7 @@ public: } // Must be called before setSurface - void setRenderAheadDepth(uint32_t renderAhead); + void setRenderAheadDepth(int renderAhead); SkISize getNextFrameSize() const; @@ -221,7 +221,7 @@ private: bool isSwapChainStuffed(); bool surfaceRequiresRedraw(); - void applyRenderAheadSettings(); + void setPresentTime(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); @@ -240,7 +240,9 @@ private: // painted onto its surface. bool mIsDirty = false; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; + bool mFixedRenderAhead = false; uint32_t mRenderAheadDepth = 0; + uint32_t mRenderAheadCapacity = 0; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index eca7d88e4e48..41cb8fdc66bd 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -60,8 +60,10 @@ static JVMAttachHook gOnStartHook = nullptr; class DisplayEventReceiverWrapper : public VsyncSource { public: - DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver) - : mDisplayEventReceiver(std::move(receiver)) {} + DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver, + const std::function<void()>& onDisplayConfigChanged) + : mDisplayEventReceiver(std::move(receiver)) + , mOnDisplayConfigChanged(onDisplayConfigChanged) {} virtual void requestNextVsync() override { status_t status = mDisplayEventReceiver->requestNextVsync(); @@ -79,6 +81,9 @@ public: case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: latest = ev.header.timestamp; break; + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: + mOnDisplayConfigChanged(); + break; } } } @@ -90,6 +95,7 @@ public: private: std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver; + std::function<void()> mOnDisplayConfigChanged; }; class DummyVsyncSource : public VsyncSource { @@ -160,22 +166,29 @@ void RenderThread::initializeDisplayEventReceiver() { // Register the FD mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); - mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver)); + mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] { + DeviceInfo::get()->onDisplayConfigChanged(); + setupFrameInterval(); + }); } else { mVsyncSource = new DummyVsyncSource(this); } } void RenderThread::initThreadLocals() { - mDisplayInfo = DeviceInfo::get()->displayInfo(); - nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps); - mTimeLord.setFrameInterval(frameIntervalNanos); - mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f); + setupFrameInterval(); initializeDisplayEventReceiver(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); mVkManager = new VulkanManager(); - mCacheManager = new CacheManager(mDisplayInfo); + mCacheManager = new CacheManager(DeviceInfo::get()->displayInfo()); +} + +void RenderThread::setupFrameInterval() { + auto& displayInfo = DeviceInfo::get()->displayInfo(); + nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / displayInfo.fps); + mTimeLord.setFrameInterval(frameIntervalNanos); + mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f); } void RenderThread::requireGlContext() { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 6bb26fd6c675..c96e284df6b4 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -102,8 +102,6 @@ public: ProfileDataContainer& globalProfileData() { return mGlobalProfileData; } Readback& readback(); - const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } - GrContext* getGrContext() const { return mGrContext.get(); } void setGrContext(sk_sp<GrContext> cxt); @@ -149,13 +147,12 @@ private: void initThreadLocals(); void initializeDisplayEventReceiver(); + void setupFrameInterval(); static int displayEventReceiverCallback(int fd, int events, void* data); void drainDisplayEventQueue(); void dispatchFrameCallbacks(); void requestVsync(); - DisplayInfo mDisplayInfo; - VsyncSource* mVsyncSource; bool mVsyncRequested; std::set<IFrameCallback*> mFrameCallbacks; diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index 210fced574e9..3f1ef93c878c 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -33,7 +33,7 @@ static size_t getCacheUsage(GrContext* grContext) { } RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { - DisplayInfo displayInfo = renderThread.mainDisplayInfo(); + DisplayInfo displayInfo = DeviceInfo::get()->displayInfo(); GrContext* grContext = renderThread.getGrContext(); ASSERT_TRUE(grContext != nullptr); diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp index 70a5f5acbb6e..da6a2604a4b6 100644 --- a/libs/hwui/tests/unit/CommonPoolTests.cpp +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -36,7 +36,9 @@ TEST(CommonPool, post) { EXPECT_TRUE(ran) << "Failed to flip atomic after 1 second"; } -TEST(CommonPool, threadCount) { +// test currently relies on timings, which +// makes it flaky. Disable for now +TEST(DISABLED_CommonPool, threadCount) { std::set<pid_t> threads; std::array<std::future<pid_t>, 64> futures; for (int i = 0; i < futures.size(); i++) { @@ -93,7 +95,9 @@ TEST(CommonPool, singleThread) { EXPECT_NE(gettid(), tid1); } -TEST(CommonPool, fullQueue) { +// Test currently relies on timings +// which makes it flaky, disable for now +TEST(DISABLED_CommonPool, fullQueue) { std::mutex lock; std::condition_variable fence; bool signaled = false; @@ -179,4 +183,4 @@ TEST(CommonPool, syncLifecycleCheck) { } CommonPool::waitForIdle(); ASSERT_EQ(0, ObjectTracker::count()); -}
\ No newline at end of file +} diff --git a/libs/protoutil/src/ProtoFileReader.cpp b/libs/protoutil/src/ProtoFileReader.cpp index 4017979fd4fc..c7f1129fbbaa 100644 --- a/libs/protoutil/src/ProtoFileReader.cpp +++ b/libs/protoutil/src/ProtoFileReader.cpp @@ -99,6 +99,7 @@ ProtoFileReader::next() // Shouldn't get to here. Always call hasNext() before calling next(). return 0; } + mPos++; return mBuffer[mOffset++]; } @@ -127,8 +128,10 @@ ProtoFileReader::move(size_t amt) if (!ensure_data()) { return; } - const size_t chunk = mMaxOffset - mOffset < amt ? amt : mMaxOffset - mOffset; + const size_t chunk = + mMaxOffset - mOffset > amt ? amt : mMaxOffset - mOffset; mOffset += chunk; + mPos += chunk; amt -= chunk; } } diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java index badffd171720..36fe8dab5dea 100644 --- a/location/java/android/location/GnssCapabilities.java +++ b/location/java/android/location/GnssCapabilities.java @@ -16,12 +16,8 @@ package android.location; -import android.annotation.IntDef; import android.annotation.SystemApi; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * A container of supported GNSS chipset capabilities. * @@ -29,53 +25,61 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi public final class GnssCapabilities { - /** The GNSS chipset supports low power mode. */ - public static final int LOW_POWER_MODE = 0; - - /** The GNSS chipset supports blacklisting satellites. */ - public static final int SATELLITE_BLACKLIST = 1; - - /** The GNSS chipset supports geofencing. */ - public static final int GEOFENCING = 2; + /** + * Bit mask indicating GNSS chipset supports low power mode. + * @hide + */ + public static final long LOW_POWER_MODE = 1L << 0; - /** The GNSS chipset supports measurements.*/ - public static final int MEASUREMENTS = 3; + /** + * Bit mask indicating GNSS chipset supports blacklisting satellites. + * @hide + */ + public static final long SATELLITE_BLACKLIST = 1L << 1; - /** The GNSS chipset supports navigation messages. */ - public static final int NAV_MESSAGES = 4; + /** + * Bit mask indicating GNSS chipset supports geofencing. + * @hide + */ + public static final long GEOFENCING = 1L << 2; - /** The GNSS chipset supports measurement corrections. */ - public static final int MEASUREMENT_CORRECTIONS = 5; + /** + * Bit mask indicating GNSS chipset supports measurements. + * @hide + */ + public static final long MEASUREMENTS = 1L << 3; - /** The GNSS chipset supports line-of-sight satellite identification measurement corrections. */ - public static final int MEASUREMENT_CORRECTIONS_LOS_SATS = 6; + /** + * Bit mask indicating GNSS chipset supports navigation messages. + * @hide + */ + public static final long NAV_MESSAGES = 1L << 4; - /** The GNSS chipset supports per satellite excess-path-length measurement corrections. */ - public static final int MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 7; + /** + * Bit mask indicating GNSS chipset supports measurement corrections. + * @hide + */ + public static final long MEASUREMENT_CORRECTIONS = 1L << 5; - /** The GNSS chipset supports reflecting planes measurement corrections. */ - public static final int MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 8; + /** + * Bit mask indicating GNSS chipset supports line-of-sight satellite identification + * measurement corrections. + * @hide + */ + public static final long MEASUREMENT_CORRECTIONS_LOS_SATS = 1L << 6; - private static final int MIN_CAPABILITY = 0; - private static final int MAX_CAPABILITY = MEASUREMENT_CORRECTIONS_REFLECTING_PLANE; + /** + * Bit mask indicating GNSS chipset supports per satellite excess-path-length + * measurement corrections. + * @hide + */ + public static final long MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 1L << 7; /** - * GNSS capability. + * Bit mask indicating GNSS chipset supports reflecting planes measurement corrections. * @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - LOW_POWER_MODE, - SATELLITE_BLACKLIST, - GEOFENCING, - MEASUREMENTS, - NAV_MESSAGES, - MEASUREMENT_CORRECTIONS, - MEASUREMENT_CORRECTIONS_LOS_SATS, - MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH, - MEASUREMENT_CORRECTIONS_REFLECTING_PLANE - }) - public @interface Capability {} + public static final long MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 1L << 8; /** @hide */ public static final long INVALID_CAPABILITIES = -1; @@ -93,60 +97,94 @@ public final class GnssCapabilities { } /** - * Returns {@code true} if the {@code capability} is supported by the GNSS implementation. + * Returns {@code true} if GNSS chipset supports low power mode, {@code false} otherwise. + */ + public boolean hasLowPowerMode() { + return hasCapability(LOW_POWER_MODE); + } + + /** + * Returns {@code true} if GNSS chipset supports blacklisting satellites, {@code false} + * otherwise. */ - public boolean hasCapability(@Capability int capability) { - return isValidCapability(capability) && (mGnssCapabilities & (1 << capability)) != 0; + public boolean hasSatelliteBlacklist() { + return hasCapability(SATELLITE_BLACKLIST); + } + + /** + * Returns {@code true} if GNSS chipset supports geofencing, {@code false} otherwise. + */ + public boolean hasGeofencing() { + return hasCapability(GEOFENCING); + } + + /** + * Returns {@code true} if GNSS chipset supports measurements, {@code false} otherwise. + */ + public boolean hasMeasurements() { + return hasCapability(MEASUREMENTS); + } + + /** + * Returns {@code true} if GNSS chipset supports navigation messages, {@code false} otherwise. + */ + public boolean hasNavMessages() { + return hasCapability(NAV_MESSAGES); + } + + /** + * Returns {@code true} if GNSS chipset supports measurement corrections, {@code false} + * otherwise. + */ + public boolean hasMeasurementCorrections() { + return hasCapability(MEASUREMENT_CORRECTIONS); + } + + /** + * Returns {@code true} if GNSS chipset supports line-of-sight satellite identification + * measurement corrections, {@code false} otherwise. + */ + public boolean hasMeasurementCorrectionsLosSats() { + return hasCapability(MEASUREMENT_CORRECTIONS_LOS_SATS); + } + + /** + * Returns {@code true} if GNSS chipset supports per satellite excess-path-length measurement + * corrections, {@code false} otherwise. + */ + public boolean hasMeasurementCorrectionsExcessPathLength() { + return hasCapability(MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH); + } + + /** + * Returns {@code true} if GNSS chipset supports reflecting planes measurement corrections, + * {@code false} otherwise. + */ + public boolean hasMeasurementCorrectionsReflectingPane() { + return hasCapability(MEASUREMENT_CORRECTIONS_REFLECTING_PLANE); } @Override public String toString() { - StringBuilder sb = new StringBuilder("GnssCapabilities: ("); - int capability = 0; - boolean addSeparator = false; - long gnssCapabilities = mGnssCapabilities; - while (gnssCapabilities != 0) { - if ((gnssCapabilities & 1) != 0) { - if (addSeparator) { - sb.append(' '); - } else { - addSeparator = true; - } - sb.append(toStringCapability(capability)); - } - gnssCapabilities >>= 1; - ++capability; + StringBuilder sb = new StringBuilder("GnssCapabilities: ( "); + if (hasLowPowerMode()) sb.append("LOW_POWER_MODE "); + if (hasSatelliteBlacklist()) sb.append("SATELLITE_BLACKLIST "); + if (hasGeofencing()) sb.append("GEOFENCING "); + if (hasMeasurements()) sb.append("MEASUREMENTS "); + if (hasNavMessages()) sb.append("NAV_MESSAGES "); + if (hasMeasurementCorrections()) sb.append("MEASUREMENT_CORRECTIONS "); + if (hasMeasurementCorrectionsLosSats()) sb.append("MEASUREMENT_CORRECTIONS_LOS_SATS "); + if (hasMeasurementCorrectionsExcessPathLength()) { + sb.append("MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH "); + } + if (hasMeasurementCorrectionsReflectingPane()) { + sb.append("MEASUREMENT_CORRECTIONS_REFLECTING_PLANE "); } sb.append(")"); return sb.toString(); } - private boolean isValidCapability(@Capability int capability) { - return capability >= MIN_CAPABILITY && capability <= MAX_CAPABILITY; - } - - private static String toStringCapability(@Capability int capability) { - switch (capability) { - case LOW_POWER_MODE: - return "LOW_POWER_MODE"; - case SATELLITE_BLACKLIST: - return "SATELLITE_BLACKLIST"; - case GEOFENCING: - return "GEOFENCING"; - case MEASUREMENTS: - return "MEASUREMENTS"; - case NAV_MESSAGES: - return "NAV_MESSAGES"; - case MEASUREMENT_CORRECTIONS: - return "MEASUREMENT_CORRECTIONS"; - case MEASUREMENT_CORRECTIONS_LOS_SATS: - return "MEASUREMENT_CORRECTIONS_LOS_SATS"; - case MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH: - return "MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH"; - case MEASUREMENT_CORRECTIONS_REFLECTING_PLANE: - return "MEASUREMENT_CORRECTIONS_REFLECTING_PLANE"; - default: - return "Unknown(" + capability + ")"; - } + private boolean hasCapability(long capability) { + return (mGnssCapabilities & capability) == capability; } } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index af60e3c67288..7a17505efa07 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1986,17 +1986,19 @@ public class LocationManager { } /** - * Returns the supported capabilities of the GNSS chipset or {@code null} if there is an error - * in obtaining the capabilities. + * Returns the supported capabilities of the GNSS chipset. + * + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present. * * @hide */ @SystemApi - public @Nullable GnssCapabilities getGnssCapabilities() { + @RequiresPermission(ACCESS_FINE_LOCATION) + public @NonNull GnssCapabilities getGnssCapabilities() { try { long gnssCapabilities = mGnssMeasurementCallbackTransport.getGnssCapabilities(); if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) { - return null; + gnssCapabilities = 0L; } return GnssCapabilities.of(gnssCapabilities); } catch (RemoteException e) { diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index a9150d4b7455..977e790eb42e 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -786,8 +786,13 @@ public final class AudioAttributes implements Parcelable { /** * Sets attributes as inferred from the legacy stream types. - * Use this method when building an {@link AudioAttributes} instance to initialize some of - * the attributes by information derived from a legacy stream type. + * Warning: do not use this method in combination with setting any other attributes such as + * usage, content type, flags or haptic control, as this method will overwrite (the more + * accurate) information describing the use case previously set in the <code>Builder</code>. + * In general, avoid using it and prefer setting usage and content type directly + * with {@link #setUsage(int)} and {@link #setContentType(int)}. + * <p>Use this method when building an {@link AudioAttributes} instance to initialize some + * of the attributes by information derived from a legacy stream type. * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL}, * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING}, * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM}, @@ -799,7 +804,8 @@ public final class AudioAttributes implements Parcelable { throw new IllegalArgumentException("STREAM_ACCESSIBILITY is not a legacy stream " + "type that was used for audio playback"); } - return setInternalLegacyStreamType(streamType); + setInternalLegacyStreamType(streamType); + return this; } /** @@ -815,7 +821,14 @@ public final class AudioAttributes implements Parcelable { AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType( streamType); if (attributes != null) { - return new Builder(attributes).setHapticChannelsMuted(mMuteHapticChannels); + mUsage = attributes.mUsage; + mContentType = attributes.mContentType; + mFlags = attributes.mFlags; + mMuteHapticChannels = attributes.areHapticChannelsMuted(); + mTags = attributes.mTags; + mBundle = attributes.mBundle; + mSource = attributes.mSource; + return this; } } switch(streamType) { diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 79d8be1a04a4..3500475eeeda 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -414,18 +414,25 @@ public final class MediaController { /** * Gets the additional session information which was set when the session was created. * - * @return The additional session information + * @return The additional session information, or {@link Bundle#EMPTY} if not set. */ - @Nullable + @NonNull public Bundle getSessionInfo() { + if (mSessionInfo != null) { + return new Bundle(mSessionInfo); + } + + // Get info from the connected session. + try { + mSessionInfo = mSessionBinder.getSessionInfo(); + } catch (RemoteException e) { + Log.d(TAG, "Dead object in getSessionInfo.", e); + } + if (mSessionInfo == null) { - try { - mSessionInfo = mSessionBinder.getSessionInfo(); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in getSessionInfo.", e); - } + mSessionInfo = Bundle.EMPTY; } - return mSessionInfo; + return new Bundle(mSessionInfo); } /** diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index d07052bb3b3f..7ee2f0a1db18 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -411,7 +411,15 @@ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransactio Region region; for (uint32_t i = 0; i < count; ++i) { - region.merge(static_cast<const Rect&>(rects[i])); + region.orSelf(static_cast<const Rect&>(rects[i])); + } + + // Hardware composer interprets a DamageRegion with a single Rect of {0,0,0,0} to be an + // undamaged region and {0,0,-1,-1} to be a fully damaged buffer. This is a confusing + // distinction for a public api. Instead, default both cases to be a fully damaged buffer. + if (count == 1 && region.getBounds().isEmpty()) { + transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION); + return; } transaction->setSurfaceDamageRegion(surfaceControl, region); diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index 34fd70332cd0..49d78b6d5871 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -108,12 +108,13 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.statusbar.car.CarFacetButton - android:id="@+id/note" + <!-- Click handling will be initialized in CarNavigationBarView because its + id = notifications which is treated special for the opening of the notification panel + --> + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/notifications" style="@style/NavigationBarButton" - systemui:icon="@drawable/car_ic_notification" - systemui:intent="intent:#Intent;component=com.android.car.notification/.CarNotificationCenterActivity;launchFlags=0x14000000;end" - systemui:packages="com.android.car.notification" + android:src="@drawable/car_ic_notification" systemui:selectedIcon="@drawable/car_ic_notification_selected" systemui:useMoreIcon="false" /> diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml index 9a074ddd7506..728a239a4ca9 100644 --- a/packages/CarSystemUI/res/layout/super_status_bar.xml +++ b/packages/CarSystemUI/res/layout/super_status_bar.xml @@ -74,17 +74,21 @@ android:layout_height="match_parent" android:layout="@layout/car_fullscreen_user_switcher"/> - <include layout="@layout/status_bar_expanded" + <include layout="@layout/notification_center_activity" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_marginBottom="@dimen/navigation_bar_height" android:visibility="invisible"/> - <include layout="@layout/notification_center_activity" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginBottom="112dp" - android:visibility="invisible" - /> + <include layout="@layout/headsup_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible"/> + + <include layout="@layout/status_bar_expanded" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible"/> <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front" diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index a0f2367c65a4..6e7be060a2cb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.car; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -34,7 +35,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; */ class CarNavigationBarView extends LinearLayout { private View mNavButtons; - private CarFacetButton mNotificationsButton; + private View mNotificationsButton; private CarStatusBar mCarStatusBar; private Context mContext; private View mLockScreenButtons; @@ -74,13 +75,39 @@ class CarNavigationBarView extends LinearLayout { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mStatusBarWindowTouchListener != null) { - // forward touch events to the status bar window so it can add a drag down + boolean shouldConsumeEvent = shouldConsumeNotificationButtonEvent(ev); + // Forward touch events to the status bar window so it can drag // windows if required (Notification shade) mStatusBarWindowTouchListener.onTouch(this, ev); + // return true if child views should not receive this event. + if (shouldConsumeEvent) { + return true; + } } return super.onInterceptTouchEvent(ev); } + /** + * If the motion event is over top of the notification button while the notification + * panel is open, we need the statusbar touch listeners handle the event instead of the button. + * Since the statusbar listener will trigger a close of the notification panel before the + * any button click events are fired this will prevent reopening the panel. + * + * Note: we can't use requestDisallowInterceptTouchEvent because the gesture detector will + * always receive the ACTION_DOWN and thus think a longpress happened if no other events are + * received + * + * @return true if the notification button should not receive the event + */ + private boolean shouldConsumeNotificationButtonEvent(MotionEvent ev) { + if (mNotificationsButton == null || !mCarStatusBar.isNotificationPanelOpen()) { + return false; + } + Rect notificationButtonLocation = new Rect(); + mNotificationsButton.getHitRect(notificationButtonLocation); + return notificationButtonLocation.contains((int) ev.getX(), (int) ev.getY()); + } + void setStatusBar(CarStatusBar carStatusBar) { mCarStatusBar = carStatusBar; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 4818227c18c9..ea29ebb88846 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -22,7 +22,10 @@ import android.animation.ValueAnimator; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; +import android.car.Car; import android.car.drivingstate.CarDrivingStateEvent; +import android.car.drivingstate.CarUxRestrictionsManager; +import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -38,10 +41,13 @@ import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import com.android.car.notification.CarHeadsUpNotificationManager; import com.android.car.notification.CarNotificationListener; import com.android.car.notification.CarNotificationView; import com.android.car.notification.CarUxRestrictionManagerWrapper; +import com.android.car.notification.HeadsUpEntry; import com.android.car.notification.NotificationClickHandlerFactory; +import com.android.car.notification.NotificationDataManager; import com.android.car.notification.NotificationViewController; import com.android.car.notification.PreprocessingManager; import com.android.internal.statusbar.RegisterStatusBarResult; @@ -343,7 +349,9 @@ public class CarStatusBar extends StatusBar implements new CloseNotificationGestureListener() { @Override protected void close() { - animateCollapsePanels(); + if (mPanelExpanded) { + animateCollapsePanels(); + } } }); // Attached to the NavBars to close the notification shade @@ -351,7 +359,9 @@ public class CarStatusBar extends StatusBar implements new NavBarCloseNotificationGestureListener() { @Override protected void close() { - animateCollapsePanels(); + if (mPanelExpanded) { + animateCollapsePanels(); + } } }); mNavBarNotificationTouchListener = @@ -404,11 +414,20 @@ public class CarStatusBar extends StatusBar implements animateCollapsePanels(); } }); + Car car = Car.createCar(mContext); + CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager) + car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); CarNotificationListener carNotificationListener = new CarNotificationListener(); CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper(); + carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager); + NotificationDataManager notificationDataManager = new NotificationDataManager(); + CarHeadsUpNotificationManager carHeadsUpNotificationManager = + new CarSystemUIHeadsUpNotificationManager(mContext, clickHandlerFactory, + notificationDataManager); + carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper, - clickHandlerFactory); + carHeadsUpNotificationManager, notificationDataManager); mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view); View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane); @@ -471,8 +490,11 @@ public class CarStatusBar extends StatusBar implements handled = closeGestureDetector.onTouchEvent(event); } boolean isTracking = mIsTracking; - Rect rect = mNotificationView.getClipBounds(); - float clippedHeight = rect.bottom; + Rect rect = mNotificationList.getClipBounds(); + float clippedHeight = 0; + if (rect != null) { + clippedHeight = rect.bottom; + } if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP && mIsSwipingVerticallyToClose) { if (mSettleClosePercentage < mPercentageFromBottom && isTracking) { @@ -502,6 +524,13 @@ public class CarStatusBar extends StatusBar implements mNotificationViewController.enable(); } + /** + * @return true if the notification panel is currently visible + */ + boolean isNotificationPanelOpen() { + return mPanelExpanded; + } + @Override public void animateExpandNotificationsPanel() { if (!mCommandQueue.panelsEnabled() || !mUserSetup) { @@ -540,11 +569,11 @@ public class CarStatusBar extends StatusBar implements /** * Animates the notification shade from one position to other. This is used to either open or - * close the notification shade completely with a velocity. Id the animation is to close the + * close the notification shade completely with a velocity. If the animation is to close the * notification shade this method also makes the view invisible after animation ends. */ private void animateNotificationPanel(float velocity, boolean isClosing) { - Rect rect = mNotificationView.getClipBounds(); + Rect rect = mNotificationList.getClipBounds(); if (rect == null) { return; } @@ -575,7 +604,7 @@ public class CarStatusBar extends StatusBar implements if (isClosing) { mStatusBarWindowController.setPanelVisible(false); mNotificationView.setVisibility(View.INVISIBLE); - mNotificationView.setClipBounds(null); + mNotificationList.setClipBounds(null); // let the status bar know that the panel is closed setPanelExpanded(false); } else { @@ -933,7 +962,18 @@ public class CarStatusBar extends StatusBar implements private void setNotificationViewClipBounds(int height) { Rect clipBounds = new Rect(); clipBounds.set(0, 0, mNotificationView.getWidth(), height); - mNotificationView.setClipBounds(clipBounds); + // Sets the clip region on the notification list view. + mNotificationList.setClipBounds(clipBounds); + + if (mNotificationView.getHeight() > 0) { + // Calculates the alpha value for the background based on how much of the notification + // shade is visible to the user. When the notification shade is completely open then + // alpha value will be 1. + float alpha = (float) height / mNotificationView.getHeight(); + Drawable background = mNotificationView.getBackground(); + + background.setAlpha((int) (alpha * 255)); + } } private void calculatePercentageFromBottom(float height) { @@ -1079,4 +1119,48 @@ public class CarStatusBar extends StatusBar implements super.onLongPress(e); } } + + /** + * SystemUi version onf the notification manager that overrides methods such that the + * notifications end up in the status bar layouts instead of a standalone window. + */ + private class CarSystemUIHeadsUpNotificationManager extends CarHeadsUpNotificationManager { + + CarSystemUIHeadsUpNotificationManager(Context context, + NotificationClickHandlerFactory clickHandlerFactory, + NotificationDataManager notificationDataManager) { + super(context, clickHandlerFactory, notificationDataManager); + } + + @Override + protected View createHeadsUpPanel() { + // In SystemUi the view is already in the window so just return a reference. + return mStatusBarWindow.findViewById(R.id.notification_headsup); + } + + @Override + protected void addHeadsUpPanelToDisplay() { + // Set the panel initial state to invisible + mHeadsUpPanel.setVisibility(View.INVISIBLE); + } + + @Override + protected void setHeadsUpVisible() { + super.setHeadsUpVisible(); + if (mHeadsUpPanel.getVisibility() == View.VISIBLE) { + mStatusBarWindowController.setHeadsUpShowing(true); + mStatusBarWindowController.setForceStatusBarVisible(true); + } + } + + @Override + protected void removeNotificationFromPanel(HeadsUpEntry currentHeadsUpNotification) { + super.removeNotificationFromPanel(currentHeadsUpNotification); + // If the panel ended up empty and hidden we can remove it from SystemUi + if (mHeadsUpPanel.getVisibility() != View.VISIBLE) { + mStatusBarWindowController.setHeadsUpShowing(false); + mStatusBarWindowController.setForceStatusBarVisible(false); + } + } + } } diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml index de3544174c6b..58deb11eef74 100644 --- a/packages/ExtServices/res/values/strings.xml +++ b/packages/ExtServices/res/values/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name">Android Services Library</string> - <string name="notification_assistant">Notification Assistant</string> + <string name="notification_assistant">Android Adaptive Notifications</string> <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string> <string-array name="autofill_field_classification_available_algorithms"> diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java index 52e54f965cc0..69e91d1b857e 100644 --- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java +++ b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java @@ -18,6 +18,7 @@ package android.ext.services.watchdog; import android.content.ComponentName; import android.content.Intent; +import android.provider.DeviceConfig; import android.service.watchdog.ExplicitHealthCheckService; import android.service.watchdog.PackageInfo; import android.util.Log; @@ -27,6 +28,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; /** * Routes explicit health check requests to the appropriate {@link ExplicitHealthChecker}. @@ -36,6 +38,10 @@ public final class ExplicitHealthCheckServiceImpl extends ExplicitHealthCheckSer // TODO: Add build dependency on NetworkStack stable AIDL so we can stop hard coding class name private static final String NETWORK_STACK_CONNECTOR_CLASS = "android.net.INetworkStackConnector"; + private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS = + "watchdog_request_timeout_millis"; + private static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = + TimeUnit.HOURS.toMillis(1); // Modified only #onCreate, using concurrent collection to ensure thread visibility private final Map<String, ExplicitHealthChecker> mSupportedCheckers = new ConcurrentHashMap<>(); @@ -70,8 +76,17 @@ public final class ExplicitHealthCheckServiceImpl extends ExplicitHealthCheckSer @Override public List<PackageInfo> onGetSupportedPackages() { List<PackageInfo> packages = new ArrayList<>(); + long requestTimeoutMillis = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS, + DEFAULT_REQUEST_TIMEOUT_MILLIS); + if (requestTimeoutMillis <= 0) { + requestTimeoutMillis = DEFAULT_REQUEST_TIMEOUT_MILLIS; + } for (ExplicitHealthChecker checker : mSupportedCheckers.values()) { - packages.add(checker.getSupportedPackage()); + PackageInfo pkg = new PackageInfo(checker.getSupportedPackageName(), + requestTimeoutMillis); + packages.add(pkg); } return packages; } @@ -87,7 +102,7 @@ public final class ExplicitHealthCheckServiceImpl extends ExplicitHealthCheckSer while (it.hasNext()) { ExplicitHealthChecker checker = it.next(); if (checker.isPending()) { - packages.add(checker.getSupportedPackage().getPackageName()); + packages.add(checker.getSupportedPackageName()); } } return packages; diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java index c51be8863d90..a982d52e59af 100644 --- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java +++ b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java @@ -16,8 +16,6 @@ package android.ext.services.watchdog; -import android.service.watchdog.PackageInfo; - /** * A type of explicit health check that can be performed on a device, e.g network health check */ @@ -40,7 +38,7 @@ interface ExplicitHealthChecker { boolean isPending(); /** - * Returns the {@link PackageInfo} object this checker can make requests for. + * Returns the name of the package this checker can make requests for. */ - PackageInfo getSupportedPackage(); + String getSupportedPackageName(); } diff --git a/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java b/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java index 09b319e81933..5722e096dff0 100644 --- a/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java +++ b/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java @@ -21,7 +21,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.service.watchdog.ExplicitHealthCheckService; -import android.service.watchdog.PackageInfo; import com.android.internal.annotations.GuardedBy; @@ -35,8 +34,6 @@ final class NetworkChecker extends ConnectivityManager.NetworkCallback private final Object mLock = new Object(); private final ExplicitHealthCheckService mService; private final String mPackageName; - // TODO: Receive from DeviceConfig flag - private final long mRequestDurationMillis = 0; @GuardedBy("mLock") private boolean mIsPending; @@ -76,8 +73,8 @@ final class NetworkChecker extends ConnectivityManager.NetworkCallback } @Override - public PackageInfo getSupportedPackage() { - return new PackageInfo(mPackageName, mRequestDurationMillis); + public String getSupportedPackageName() { + return mPackageName; } // TODO(b/120598832): Also monitor NetworkCallback#onAvailable to see if we have any diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml index 90f96e0f7a18..478ed6b06596 100644 --- a/packages/NetworkStack/res/values/config.xml +++ b/packages/NetworkStack/res/values/config.xml @@ -7,6 +7,9 @@ values are meant to be the default when no other configuration is specified. --> + <!-- DNS probe timeout for network validation. Enough for 3 DNS queries 5 seconds apart. --> + <integer name="default_captive_portal_dns_probe_timeout">12500</integer> + <!-- HTTP URL for network validation, to use for detecting captive portals. --> <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string> @@ -27,6 +30,7 @@ <!-- Configuration hooks for the above settings. Empty by default but may be overridden by RROs. --> + <integer name="config_captive_portal_dns_probe_timeout"></integer> <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> <string name="config_captive_portal_http_url" translatable="false"></string> <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> @@ -35,4 +39,8 @@ </string-array> <string-array name="config_captive_portal_fallback_probe_specs" translatable="false"> </string-array> -</resources>
\ No newline at end of file + + <!-- Customized default DNS Servers address. --> + <string-array name="config_default_dns_servers" translatable="false"> + </string-array> +</resources> diff --git a/packages/NetworkStack/res/values/overlayable.xml b/packages/NetworkStack/res/values/overlayable.xml index 7eeeefcf87f6..b9d5337ec572 100644 --- a/packages/NetworkStack/res/values/overlayable.xml +++ b/packages/NetworkStack/res/values/overlayable.xml @@ -17,9 +17,12 @@ <overlayable name="NetworkStackConfig"> <policy type="product|system|vendor"> <!-- Configuration values for NetworkMonitor --> + <item type="integer" name="config_captive_portal_dns_probe_timeout"/> <item type="string" name="config_captive_portal_http_url"/> <item type="string" name="config_captive_portal_https_url"/> <item type="array" name="config_captive_portal_fallback_urls"/> + <!-- Configuration value for DhcpResults --> + <item type="array" name="config_default_dns_servers"/> </policy> </overlayable> </resources> diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java index af0e3bb8bfc2..ca6c17a65e99 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java @@ -45,6 +45,7 @@ import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; import android.content.Context; import android.net.DhcpResults; +import android.net.InetAddresses; import android.net.TrafficStats; import android.net.ip.IpClient; import android.net.metrics.DhcpClientEvent; @@ -67,6 +68,7 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.TrafficStatsConstants; import com.android.internal.util.WakeupMessage; +import com.android.networkstack.R; import java.io.FileDescriptor; import java.io.IOException; @@ -500,6 +502,18 @@ public class DhcpClient extends StateMachine { private void acceptDhcpResults(DhcpResults results, String msg) { mDhcpLease = results; + if (mDhcpLease.dnsServers.isEmpty()) { + // supplement customized dns servers + String[] dnsServersList = + mContext.getResources().getStringArray(R.array.config_default_dns_servers); + for (final String dnsServer : dnsServersList) { + try { + mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid default DNS server: " + dnsServer, e); + } + } + } mOffer = null; Log.d(TAG, msg + " lease: " + mDhcpLease); notifySuccess(); diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java index d21b5f7853bb..b8ab94ce3830 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java @@ -647,4 +647,9 @@ public class DhcpServer extends IDhcpServer.Stub { } } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 80d139cb6153..96e09fafb6b6 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -557,6 +557,11 @@ public class IpClient extends StateMachine { checkNetworkStackCallingPermission(); IpClient.this.removeKeepalivePacketFilter(slot); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } public String getInterfaceName() { diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java index 822678717092..2934e1cf0c82 100644 --- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java @@ -35,6 +35,77 @@ public class NetworkStackUtils { // TODO: Refer to DeviceConfig definition. public static final String NAMESPACE_CONNECTIVITY = "connectivity"; + /** + * A list of captive portal detection specifications used in addition to the fallback URLs. + * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated + * by "@@,@@". + */ + public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = + "captive_portal_fallback_probe_specs"; + + /** + * A comma separated list of URLs used for captive portal detection in addition to the + * fallback HTTP url associated with the CAPTIVE_PORTAL_FALLBACK_URL settings. + */ + public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = + "captive_portal_other_fallback_urls"; + + /** + * Which User-Agent string to use in the header of the captive portal detection probes. + * The User-Agent field is unset when this setting has no value (HttpUrlConnection default). + */ + public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; + + /** + * Whether to use HTTPS for network validation. This is enabled by default and the setting + * needs to be set to 0 to disable it. This setting is a misnomer because captive portals + * don't actually use HTTPS, but it's consistent with the other settings. + */ + public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; + + /** + * The URL used for HTTPS captive portal detection upon a new connection. + * A 204 response code from the server is used for validation. + */ + public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; + + /** + * The URL used for HTTP captive portal detection upon a new connection. + * A 204 response code from the server is used for validation. + */ + public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; + + /** + * The URL used for fallback HTTP captive portal detection when previous HTTP + * and HTTPS captive portal detection attemps did not return a conclusive answer. + */ + public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; + + /** + * What to do when connecting a network that presents a captive portal. + * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. + * + * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. + */ + public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; + + /** + * Don't attempt to detect captive portals. + */ + public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; + + /** + * When detecting a captive portal, display a notification that + * prompts the user to sign in. + */ + public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; + + /** + * When detecting a captive portal, immediately disconnect from the + * network and do not reconnect to that network in the future. + */ + public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; + static { System.loadLibrary("networkstackutilsjni"); } diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index a0a90fde518f..a6d74842f631 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -251,6 +251,11 @@ public class NetworkStackService extends Service { } } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } private static class NetworkMonitorImpl extends INetworkMonitor.Stub { @@ -325,5 +330,10 @@ public class NetworkStackService extends Service { checkNetworkStackCallingPermission(); mNm.notifyNetworkCapabilitiesChanged(nc); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 27d420328017..bacec78e5699 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -43,6 +43,16 @@ import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPE import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS; import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS; import static android.net.util.DataStallUtils.DEFAULT_DNS_LOG_SIZE; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_URL; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTPS_URL; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTP_URL; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_IGNORE; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_PROMPT; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USER_AGENT; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; import static android.net.util.NetworkStackUtils.NAMESPACE_CONNECTIVITY; import static android.net.util.NetworkStackUtils.isEmpty; @@ -55,6 +65,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.LinkProperties; @@ -118,6 +129,7 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; /** @@ -132,8 +144,13 @@ public class NetworkMonitor extends StateMachine { + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/60.0.3112.32 Safari/537.36"; + @VisibleForTesting + static final String CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT = + "captive_portal_dns_probe_timeout"; + private static final int SOCKET_TIMEOUT_MS = 10000; private static final int PROBE_TIMEOUT_MS = 3000; + enum EvaluationResult { VALIDATED(true), CAPTIVE_PORTAL(false); @@ -1164,20 +1181,47 @@ public class NetworkMonitor extends StateMachine { } private boolean getIsCaptivePortalCheckEnabled() { - String symbol = Settings.Global.CAPTIVE_PORTAL_MODE; - int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT; + String symbol = CAPTIVE_PORTAL_MODE; + int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT; int mode = mDependencies.getSetting(mContext, symbol, defaultValue); - return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE; + return mode != CAPTIVE_PORTAL_MODE_IGNORE; } private boolean getUseHttpsValidation() { - return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; + return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, + CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; } private String getCaptivePortalServerHttpsUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_https_url, - R.string.default_captive_portal_https_url, - Settings.Global.CAPTIVE_PORTAL_HTTPS_URL); + R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL); + } + + private int getDnsProbeTimeout() { + return getIntSetting(mContext, R.integer.config_captive_portal_dns_probe_timeout, + CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout); + } + + /** + * Gets an integer setting from resources or device config + * + * configResource is used if set, followed by device config if set, followed by defaultResource. + * If none of these are set then an exception is thrown. + * + * TODO: move to a common location such as a ConfigUtils class. + * TODO(b/130324939): test that the resources can be overlayed by an RRO package. + */ + @VisibleForTesting + int getIntSetting(@NonNull final Context context, @StringRes int configResource, + @NonNull String symbol, @StringRes int defaultResource) { + final Resources res = context.getResources(); + try { + return res.getInteger(configResource); + } catch (Resources.NotFoundException e) { + return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, + symbol, res.getInteger(defaultResource)); + } } /** @@ -1189,8 +1233,7 @@ public class NetworkMonitor extends StateMachine { */ public String getCaptivePortalServerHttpUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_http_url, - R.string.default_captive_portal_http_url, - Settings.Global.CAPTIVE_PORTAL_HTTP_URL); + R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL); } private int getConsecutiveDnsTimeoutThreshold() { @@ -1219,13 +1262,13 @@ public class NetworkMonitor extends StateMachine { private URL[] makeCaptivePortalFallbackUrls() { try { - final String firstUrl = mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, null); + final String firstUrl = mDependencies.getSetting(mContext, CAPTIVE_PORTAL_FALLBACK_URL, + null); final URL[] settingProviderUrls; if (!TextUtils.isEmpty(firstUrl)) { - final String otherUrls = mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, ""); + final String otherUrls = mDependencies.getDeviceConfigProperty( + NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, ""); // otherUrls may be empty, but .split() ignores trailing empty strings final String separator = ","; final String[] urls = (firstUrl + separator + otherUrls).split(separator); @@ -1245,8 +1288,9 @@ public class NetworkMonitor extends StateMachine { private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() { try { - final String settingsValue = mDependencies.getSetting( - mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); + final String settingsValue = mDependencies.getDeviceConfigProperty( + NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); + final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0]; final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue) ? emptySpecs @@ -1340,8 +1384,8 @@ public class NetworkMonitor extends StateMachine { } private String getCaptivePortalUserAgent() { - return mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT); + return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY, + CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT); } private URL nextFallbackUrl() { @@ -1440,6 +1484,45 @@ public class NetworkMonitor extends StateMachine { return sendHttpProbe(url, probeType, null); } + /** Do a DNS lookup for the given server, or throw UnknownHostException after timeoutMs */ + @VisibleForTesting + protected InetAddress[] sendDnsProbeWithTimeout(String host, int timeoutMs) + throws UnknownHostException { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference<List<InetAddress>> resultRef = new AtomicReference<>(); + final DnsResolver.Callback<List<InetAddress>> callback = + new DnsResolver.Callback<List<InetAddress>>() { + public void onAnswer(List<InetAddress> answer, int rcode) { + if (rcode == 0) { + resultRef.set(answer); + } + latch.countDown(); + } + public void onError(@NonNull DnsResolver.DnsException e) { + validationLog("DNS error resolving " + host + ": " + e.getMessage()); + latch.countDown(); + } + }; + + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); + mDependencies.getDnsResolver().query(mNetwork, host, DnsResolver.FLAG_EMPTY, + r -> r.run() /* executor */, null /* cancellationSignal */, callback); + TrafficStats.setThreadStatsTag(oldTag); + + try { + latch.await(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + + List<InetAddress> result = resultRef.get(); + if (result == null || result.size() == 0) { + throw new UnknownHostException(host); + } + + return result.toArray(new InetAddress[0]); + } + /** Do a DNS resolution of the given server. */ private void sendDnsProbe(String host) { if (TextUtils.isEmpty(host)) { @@ -1451,7 +1534,7 @@ public class NetworkMonitor extends StateMachine { int result; String connectInfo; try { - InetAddress[] addresses = mNetwork.getAllByName(host); + InetAddress[] addresses = sendDnsProbeWithTimeout(host, getDnsProbeTimeout()); StringBuffer buffer = new StringBuffer(); for (InetAddress address : addresses) { buffer.append(',').append(address.getHostAddress()); @@ -1776,6 +1859,10 @@ public class NetworkMonitor extends StateMachine { return new OneAddressPerFamilyNetwork(network); } + public DnsResolver getDnsResolver() { + return DnsResolver.getInstance(); + } + public Random getRandom() { return new Random(); } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java index bee4bbd9f42d..6a6bf83bd3c8 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java @@ -494,4 +494,9 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub { listener.onComplete(makeStatus(ERROR_INTERNAL_INTERRUPTED)); return true; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java index 2775fde4c8b9..bea7052d8af2 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java @@ -91,6 +91,11 @@ public final class RegularMaintenanceJobService extends JobService { } @Override + public int getInterfaceVersion() { + return this.VERSION; + } + + @Override public IBinder asBinder() { return null; } diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java index 7d5e9e3ba174..f0e2f1b8d459 100644 --- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java +++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java @@ -133,6 +133,11 @@ public class DhcpServerTest { public void onStatusAvailable(int statusCode) { assertEquals(STATUS_SUCCESS, statusCode); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; @Before diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index 910bdc7e600f..0dc1cbf8a984 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -26,10 +26,14 @@ import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; +import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -38,6 +42,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; @@ -52,6 +57,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkMonitorCallbacks; import android.net.InetAddresses; import android.net.LinkProperties; @@ -66,6 +72,7 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.ConditionVariable; import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; @@ -76,6 +83,7 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.networkstack.R; import com.android.networkstack.metrics.DataStallDetectionStats; import com.android.networkstack.metrics.DataStallStatsUtils; @@ -93,8 +101,12 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; +import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Random; +import java.util.concurrent.Executor; import javax.net.ssl.SSLHandshakeException; @@ -108,6 +120,7 @@ public class NetworkMonitorTest { private @Mock IpConnectivityLog mLogger; private @Mock SharedLog mValidationLogger; private @Mock NetworkInfo mNetworkInfo; + private @Mock DnsResolver mDnsResolver; private @Mock ConnectivityManager mCm; private @Mock TelephonyManager mTelephony; private @Mock WifiManager mWifi; @@ -153,14 +166,40 @@ public class NetworkMonitorTest { private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + private void setDnsAnswers(String[] answers) throws UnknownHostException { + if (answers == null) { + doThrow(new UnknownHostException()).when(mNetwork).getAllByName(any()); + doNothing().when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any()); + return; + } + + List<InetAddress> answerList = new ArrayList<>(); + for (String answer : answers) { + answerList.add(InetAddresses.parseNumericAddress(answer)); + } + InetAddress[] answerArray = answerList.toArray(new InetAddress[0]); + + doReturn(answerArray).when(mNetwork).getAllByName(any()); + + doAnswer((invocation) -> { + Executor executor = (Executor) invocation.getArgument(3); + DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(5); + new Handler(Looper.getMainLooper()).post(() -> { + executor.execute(() -> callback.onAnswer(answerList, 0)); + }); + return null; + }).when(mDnsResolver).query(eq(mNetwork), any(), anyInt(), any(), any(), any()); + } + @Before public void setUp() throws IOException { MockitoAnnotations.initMocks(this); when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mNetwork); + when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver); when(mDependencies.getRandom()).thenReturn(mRandom); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS), + when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS), anyInt())).thenReturn(1); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) .thenReturn(TEST_HTTP_URL); @@ -201,9 +240,8 @@ public class NetworkMonitorTest { }).when(mNetwork).openConnection(any()); when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - doReturn(new InetAddress[] { - InetAddresses.parseNumericAddress("192.168.0.0") - }).when(mNetwork).getAllByName(any()); + + setDnsAnswers(new String[]{"2001:db8::1", "192.0.2.2"}); when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { mRegisteredReceivers.add(invocation.getArgument(0)); @@ -310,6 +348,44 @@ public class NetworkMonitorTest { } @Test + public void testGetIntSetting() throws Exception { + WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); + + // No config resource, no device config. Expect to get default resource. + doThrow(new Resources.NotFoundException()) + .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)); + doAnswer(invocation -> { + int defaultValue = invocation.getArgument(2); + return defaultValue; + }).when(mDependencies).getDeviceConfigPropertyInt(any(), + eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), + anyInt()); + when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout))) + .thenReturn(42); + assertEquals(42, wnm.getIntSetting(mContext, + R.integer.config_captive_portal_dns_probe_timeout, + NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout)); + + // Set device config. Expect to get device config. + when(mDependencies.getDeviceConfigPropertyInt(any(), + eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt())) + .thenReturn(1234); + assertEquals(1234, wnm.getIntSetting(mContext, + R.integer.config_captive_portal_dns_probe_timeout, + NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout)); + + // Set config resource. Expect to get config resource. + when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) + .thenReturn(5678); + assertEquals(5678, wnm.getIntSetting(mContext, + R.integer.config_captive_portal_dns_probe_timeout, + NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, + R.integer.default_captive_portal_dns_probe_timeout)); + } + + @Test public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException { setSslException(mHttpsConnection); setPortal302(mHttpConnection); @@ -639,6 +715,45 @@ public class NetworkMonitorTest { runPartialConnectivityNetworkTest(); } + private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { + String[] actualStrings = new String[actual.length]; + for (int i = 0; i < actual.length; i++) { + actualStrings[i] = actual[i].getHostAddress(); + } + assertArrayEquals("Array of IP addresses differs", expected, actualStrings); + } + + @Test + public void testSendDnsProbeWithTimeout() throws Exception { + WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); + final int shortTimeoutMs = 200; + + String[] expected = new String[]{"2001:db8::"}; + setDnsAnswers(expected); + InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + assertIpAddressArrayEquals(expected, actual); + + expected = new String[]{"2001:db8::", "192.0.2.1"}; + setDnsAnswers(expected); + actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + assertIpAddressArrayEquals(expected, actual); + + expected = new String[0]; + setDnsAnswers(expected); + try { + wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + fail("No DNS results, expected UnknownHostException"); + } catch (UnknownHostException e) { + } + + setDnsAnswers(null); + try { + wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); + fail("DNS query timed out, expected UnknownHostException"); + } catch (UnknownHostException e) { + } + } + private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { for (int i = 0; i < count; i++) { wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( @@ -683,13 +798,13 @@ public class NetworkMonitorTest { } private void setOtherFallbackUrls(String urls) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); + when(mDependencies.getDeviceConfigProperty(any(), + eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); } private void setFallbackSpecs(String specs) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); + when(mDependencies.getDeviceConfigProperty(any(), + eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); } private void setCaptivePortalMode(int mode) { diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java index a00eff7992d4..87346e5d6a28 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java @@ -136,6 +136,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -156,6 +161,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -178,6 +188,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -200,6 +215,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } @@ -219,6 +239,11 @@ public class IpMemoryStoreServiceTest { public IBinder asBinder() { return null; } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } diff --git a/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect1_copy.xml b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect1_copy.xml new file mode 100644 index 000000000000..28265ea6177b --- /dev/null +++ b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect1_copy.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Copy of progress_indeterminate_horizontal_rect1 in frameworks/base/core/res --> +<set xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="2000" + android:propertyXName="translateX" + android:pathData="M -522.59998,0 c 48.89972,0 166.02656,0 301.21729,0 c 197.58128,0 420.9827,0 420.9827,0 " + android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_translatex_copy" + android:repeatCount="infinite" /> + <objectAnimator + android:duration="2000" + android:propertyYName="scaleX" + android:pathData="M 0 0.1 L 1 0.826849212646 L 2 0.1" + android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_scalex_copy" + android:repeatCount="infinite" /> +</set> diff --git a/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect2_copy.xml b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect2_copy.xml new file mode 100644 index 000000000000..24cc452743bb --- /dev/null +++ b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect2_copy.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Copy of progress_indeterminate_horizontal_rect2 in frameworks/base/core/res --> +<set xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="2000" + android:propertyXName="translateX" + android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0 " + android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_translatex_copy" + android:repeatCount="infinite" /> + <objectAnimator + android:duration="2000" + android:propertyYName="scaleX" + android:pathData="M 0.0,0.1 L 1.0,0.571379510698 L 2.0,0.909950256348 L 3.0,0.1" + android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_scalex_copy" + android:repeatCount="infinite" /> +</set> diff --git a/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml index 2b7535ab91d1..4bf639d2960c 100644 --- a/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml +++ b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml @@ -14,15 +14,13 @@ limitations under the License. --> -<!-- Variant of progress_indeterminate_horizontal_material in frameworks/base/core/res, which - draws the whole height of the progress bar instead having blank space above and below the - bar. --> +<!-- Copy of progress_indeterminate_horizontal_material_trimmed in frameworks/base/core/res --> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" > <target android:name="rect2_grp" - android:animation="@*android:anim/progress_indeterminate_horizontal_rect2" /> + android:animation="@anim/progress_indeterminate_horizontal_rect2_copy" /> <target android:name="rect1_grp" - android:animation="@*android:anim/progress_indeterminate_horizontal_rect1" /> + android:animation="@anim/progress_indeterminate_horizontal_rect1_copy" /> </animated-vector>
\ No newline at end of file diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_scalex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_scalex_copy.xml new file mode 100644 index 000000000000..d808f4f77a3b --- /dev/null +++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_scalex_copy.xml @@ -0,0 +1,18 @@ +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Copy of progress_indeterminate_horizontal_rect1_scalex in frameworks/base/core/res --> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0 0 L 0.3665 0 C 0.47252618112021,0.062409910275 0.61541608570164,0.5 0.68325,0.5 C 0.75475061236836,0.5 0.75725829093844,0.814510098964 1.0,1.0" /> diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_translatex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_translatex_copy.xml new file mode 100644 index 000000000000..e78d5e73c048 --- /dev/null +++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_translatex_copy.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Copy of progress_indeterminate_horizontal_rect1_translatex in frameworks/base/core/res --> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 L 0.2 0 C 0.3958333333336,0.0 0.474845090492,0.206797621729 0.5916666666664,0.417082932942 C 0.7151610251224,0.639379624869 0.81625,0.974556908664 1.0,1.0 " /> diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_scalex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_scalex_copy.xml new file mode 100644 index 000000000000..43c4124fcf96 --- /dev/null +++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_scalex_copy.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Copy of progress_indeterminate_horizontal_rect2_scalex in frameworks/base/core/res --> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0,0 C 0.06834272400867,0.01992566661414 0.19220331656133,0.15855429260523 0.33333333333333,0.34926160892842 C 0.38410433133433,0.41477913453861 0.54945792615267,0.68136029463551 0.66666666666667,0.68279962777002 C 0.752586273196,0.68179620963216 0.737253971954,0.878896194318 1,1" /> diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_translatex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_translatex_copy.xml new file mode 100644 index 000000000000..615d6d60dfd8 --- /dev/null +++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_translatex_copy.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Copy of progress_indeterminate_horizontal_rect2_translatex in frameworks/base/core/res --> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 C 0.0375,0.0 0.128764607715,0.0895380946618 0.25,0.218553507947 C 0.322410320025,0.295610602487 0.436666666667,0.417591408114 0.483333333333,0.489826169306 C 0.69,0.80972296795 0.793333333333,0.950016125212 1.0,1.0 " /> diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml index 1a47afc81d55..f532caa98203 100644 --- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml +++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml @@ -42,6 +42,7 @@ android:layout_height="wrap_content" android:gravity="start|center_vertical" android:minWidth="56dp" + android:minHeight="48dp" android:orientation="horizontal" android:clipToPadding="false" android:paddingTop="4dp" diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index a5b78497a645..cd97ce88f500 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -4350,15 +4350,20 @@ public class SettingsProvider extends ContentProvider { } if (navBarMode != -1) { + String overlayPackage = ""; try { - overlayManager.setEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY, - navBarMode == NAV_BAR_MODE_3BUTTON, - UserHandle.USER_CURRENT); - overlayManager.setEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY, - navBarMode == NAV_BAR_MODE_2BUTTON, - UserHandle.USER_CURRENT); - overlayManager.setEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY, - navBarMode == NAV_BAR_MODE_GESTURAL, + switch (navBarMode) { + case NAV_BAR_MODE_3BUTTON: + overlayPackage = NAV_BAR_MODE_3BUTTON_OVERLAY; + break; + case NAV_BAR_MODE_2BUTTON: + overlayPackage = NAV_BAR_MODE_2BUTTON_OVERLAY; + break; + case NAV_BAR_MODE_GESTURAL: + overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY; + break; + } + overlayManager.setEnabledExclusiveInCategory(overlayPackage, UserHandle.USER_CURRENT); } catch (RemoteException e) { throw new IllegalStateException( diff --git a/packages/SystemUI/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md new file mode 100644 index 000000000000..5e4f6c79f47a --- /dev/null +++ b/packages/SystemUI/docs/clock-plugins.md @@ -0,0 +1,92 @@ +# Clock Plugins + +## Introduction + +The clock appearing on the lock screen and always on display (AOD) can be +customized via the ClockPlugin plugin interface. + +## System Health + +Clocks are high risk for battery consumption and screen burn-in because they +modify the UI of AOD. + +To reduce battery consumption, it is recommended to +target a maximum on-pixel-ratio (OPR) of 5%. Clocks that are composed of +large blocks of color that cause the OPR to exceed 5% should be avoided. + +To prevent screen burn-in, clocks should not be composed of large solid +blocks of color, and the clock should be moved around the screen to +distribute the on pixels across a large number of pixels. Software +burn-in testing is a good starting point to assess the pixel shifting +(clock movement) scheme and shape of the clock. + +### Software Burn-In Test + +The goal is to look for bright spots in the luminosity average over a period of +time. It is difficult to define a threshold where burn-in will occur. It is, +therefore, recommended to compare against an element on AOD that is known not +to cause problems. + +For clock face that contain color, it is recommended to use an all white +version of the face. Since white has the highest luminosity, this version of +the clock face represents the worst case scenario. + +To start, generate a sequence of screenshots for each minute over a 12 hr interval. + +``` +serial = '84TY004MS' # serial number for the device +count = 1 +t = datetime.datetime(2019, 1, 1) +stop = t + datetime.timedelta(hours=12) +if not os.path.exists(OUTPUT_FOLDER): + raise RuntimeError('output folder "%s" does not exist' % OUTPUT_FOLDER) +while t <= stop: + os.system("adb -s %s shell 'date %s ; am broadcast -a android.intent.action.TIME_SET'" % (serial, t.strftime('%m%d%H%M%Y.%S'))) + os.system('adb -s %s shell screencap -p > %s/screencap_%06d.png' % (serial, OUTPUT_FOLDER, count)) + t += datetime.timedelta(minutes=1) + count += 1 +``` + +Average the luminosity of the screenshots. + +``` +#!python +import numpy +import scipy.ndimage +from imageio import imread, imwrite +import matplotlib.pylab as plt +import os +import os.path + +def images(path): + return [os.path.join(path, name) for name in os.listdir(path) if name.endswith('.png')] + +def average(images): + AVG = None + for name in images: + IM = scipy.ndimage.imread(name, mode='L') + A = numpy.array(IM, dtype=numpy.double) + if AVG is None: + AVG = A + else: + AVG += A + AVG /= len(images) + return numpy.array(AVG, dtype=numpy.uint8) + +def main(path): + ims = images(path) + if len(ims) == 0: + raise ValueError("folder '%s' doesn't contain any png files" % path) + AVG = average(ims) + imwrite('average.png', AVG) + plt.imshow(AVG) + plt.show() + +if __name__=='__main__': + import sys + main(sys.argv[1]) +``` + +Look for bright spots in the luminosity average. If bright spots are found, +action should be taken to change the shape of the clock face or increase the +amount of pixel shifting. diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png Binary files differindex 83d714bfcb05..4a0972fceb18 100644 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png Binary files differindex 8d0e6ed493e6..a7d5a0d0e86a 100644 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png Binary files differindex 1ac01135c211..812dc9df5ea1 100644 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 463367b2c600..86ed9e3c8331 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -33,6 +33,21 @@ <include android:id="@+id/default_clock_view" layout="@layout/text_clock" /> + <TextClock + android:id="@+id/default_clock_view_bold" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal" + android:letterSpacing="0.03" + android:textColor="?attr/wallpaperTextColor" + android:singleLine="true" + style="@style/widget_big_bold" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + android:elegantTextHeight="false" + android:visibility="gone" + /> </FrameLayout> <include layout="@layout/keyguard_status_area" android:id="@+id/keyguard_status_area" diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 8bbc270573f1..6e4416a9caa2 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -66,6 +66,13 @@ <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item> <item name="android:ellipsize">none</item> </style> + <style name="widget_big_bold"> + <item name="android:textStyle">bold</item> + <item name="android:textSize">@dimen/widget_big_font_size</item> + <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:ellipsize">none</item> + </style> <style name="BouncerSecurityContainer"> <item name="android:layout_gravity">center_horizontal|bottom</item> diff --git a/packages/SystemUI/res/drawable/button_border_selected.xml b/packages/SystemUI/res/drawable/button_border_selected.xml new file mode 100644 index 000000000000..d9299ec0622b --- /dev/null +++ b/packages/SystemUI/res/drawable/button_border_selected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid + android:color="@color/notification_guts_selection_bg" /> + <stroke + android:width="2dp" + android:color="?android:attr/colorAccent"/> + <corners android:radius="@dimen/rect_button_radius" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/button_border_unselected.xml b/packages/SystemUI/res/drawable/button_border_unselected.xml new file mode 100644 index 000000000000..4ea37640780f --- /dev/null +++ b/packages/SystemUI/res/drawable/button_border_unselected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle" + android:color="@color/notification_guts_selection_bg"> + <stroke + android:width="2dp" + android:color="@color/GM2_grey_300"/> + + <corners android:radius="@dimen/rect_button_radius" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/button_ripple_radius.xml b/packages/SystemUI/res/drawable/button_ripple_radius.xml new file mode 100644 index 000000000000..5c2857a4765c --- /dev/null +++ b/packages/SystemUI/res/drawable/button_ripple_radius.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<ripple + xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <solid android:color="@color/notification_guts_selection_bg" /> + <corners android:radius="@dimen/rect_button_radius" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_notification_gentle.xml b/packages/SystemUI/res/drawable/ic_notification_gentle.xml index 7074130a63ce..de54b1040b03 100644 --- a/packages/SystemUI/res/drawable/ic_notification_gentle.xml +++ b/packages/SystemUI/res/drawable/ic_notification_gentle.xml @@ -18,18 +18,18 @@ Copyright (C) 2019 The Android Open Source Project android:id="@+id/back"> <shape android:shape="oval"> <solid - android:color="@color/GM2_green_500" /> + android:color="@color/notification_silence_color" /> <size - android:height="36dp" - android:width="36dp"/> + android:height="24dp" + android:width="24dp"/> </shape> </item> <item android:id="@+id/fore" android:gravity="center"> <vector - android:width="32dp" - android:height="32dp" + android:width="13dp" + android:height="13dp" android:viewportWidth="24" android:viewportHeight="24"> <path diff --git a/packages/SystemUI/res/drawable/ic_notification_interruptive.xml b/packages/SystemUI/res/drawable/ic_notification_interruptive.xml index 0a8b3b8fa775..f49aa4a198bd 100644 --- a/packages/SystemUI/res/drawable/ic_notification_interruptive.xml +++ b/packages/SystemUI/res/drawable/ic_notification_interruptive.xml @@ -19,18 +19,18 @@ Copyright (C) 2019 The Android Open Source Project android:id="@+id/back"> <shape android:shape="oval"> <solid - android:color="@color/GM2_yellow_500" /> + android:color="@color/notification_alert_color" /> <size - android:height="36dp" - android:width="36dp"/> + android:height="24dp" + android:width="24dp"/> </shape> </item> <item android:id="@+id/fore" android:gravity="center"> <vector - android:width="32dp" - android:height="32dp" + android:width="13dp" + android:height="13dp" android:viewportWidth="24" android:viewportHeight="24"> <path diff --git a/packages/SystemUI/res/drawable/ic_volume_odi_captions.xml b/packages/SystemUI/res/drawable/ic_volume_odi_captions.xml index 675aee9cd1dd..719b70219c4a 100644 --- a/packages/SystemUI/res/drawable/ic_volume_odi_captions.xml +++ b/packages/SystemUI/res/drawable/ic_volume_odi_captions.xml @@ -18,24 +18,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - <path - android:pathData="M16,12h2v2h-2z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M6,12h8v2h-8z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M20,2C21.1046,2 22,2.8954 22,4L22,16C22,17.1046 21.1046,18 20,18L6,18L2,22L2,4C2,2.8954 2.8954,2 4,2L20,2ZM20,16L20,4L4,4L4,16L20,16Z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M6,8h2v2h-2z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M10,8h8v2h-8z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> + <path + android:fillColor="#FF000000" + android:pathData="M20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,6h16v12zM6,10h2v2L6,12zM6,14h8v2L6,16zM16,14h2v2h-2zM10,10h8v2h-8z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml b/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml index e818455950de..953d4fef6e58 100644 --- a/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml +++ b/packages/SystemUI/res/drawable/ic_volume_odi_captions_disabled.xml @@ -18,20 +18,16 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - <path - android:pathData="M2.07,0.64L22,20.59L20.6,22L16.6,18L6,18L2,22L2,4C2.0006,3.8236 2.0276,3.6484 2.08,3.48L0.66,2.05L2.07,0.64ZM5.17,16L14.6,16L12.6,14L6,14L6,12L10.6,12L8,9.4L8,10L6,10L6,8L6.6,8L4,5.4L4,16L5.17,16Z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M18,12l-1.74,0l1.74,1.74z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M18,8l-5.74,0l2,2l3.74,0z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> - <path - android:pathData="M20,4L20,15.74L21.53,17.27C21.8296,16.9142 21.9958,16.4651 22,16L22,4C22,2.8954 21.1046,2 20,2L6.26,2L8.26,4L20,4Z" - android:fillColor="#1A73E8" - android:fillType="nonZero"/> + <path + android:fillColor="#FF000000" + android:pathData="M18,10l-5.18,0l2,2l3.18,0z"/> + <path + android:fillColor="#FF000000" + android:pathData="M18,14l-1.19,0l1.19,1.19z"/> + <path + android:fillColor="#FF000000" + android:pathData="M2,2L0.58,3.41l1.66,1.66C2.09,5.35 2,5.66 2,6v12c0,1.1 0.9,2 2,2h13.17l2.61,2.61l1.41,-1.41L2,2zM4,18V6.83L7.17,10H6v2h2v-1.17L11.17,14H6v2h7.17l2,2H4z"/> + <path + android:fillColor="#FF000000" + android:pathData="M20,4H6.82l2,2H20v11.19l1.75,1.75C21.91,18.66 22,18.34 22,18V6C22,4.9 21.1,4 20,4z"/> </vector> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index f7c6c435d258..a94ae0f7223b 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -22,7 +22,7 @@ android:layout_height="wrap_content" android:clickable="true" android:clipChildren="false" - android:clipToPadding="false" + android:clipToPadding="true" android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:background="@color/notification_guts_bg_color"> @@ -39,69 +39,62 @@ android:layout_width="@dimen/notification_guts_header_height" android:layout_height="@dimen/notification_guts_header_height" android:layout_centerVertical="true" + android:layout_alignParentStart="true" android:layout_marginEnd="3dp" /> <TextView android:id="@+id/pkgname" android:layout_width="wrap_content" android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationInfo.Primary" + android:layout_centerVertical="true" + style="@style/TextAppearance.NotificationImportanceHeader" android:layout_marginStart="3dp" android:layout_marginEnd="2dp" - android:singleLine="true" - android:layout_centerVertical="true" - android:layout_toEndOf="@id/pkgicon" /> + android:layout_toEndOf="@id/pkgicon" + android:singleLine="true" /> <TextView android:id="@+id/pkg_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationInfo.Primary" + android:layout_centerVertical="true" + style="@style/TextAppearance.NotificationImportanceHeader" android:layout_marginStart="2dp" android:layout_marginEnd="2dp" - android:text="@*android:string/notification_header_divider_symbol" - android:layout_centerVertical="true" - android:layout_toEndOf="@id/pkgname" /> + android:layout_toEndOf="@id/pkgname" + android:text="@*android:string/notification_header_divider_symbol" /> <TextView android:id="@+id/delegate_name" android:layout_width="wrap_content" android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationInfo.Primary" + android:layout_centerVertical="true" + style="@style/TextAppearance.NotificationImportanceHeader" android:layout_marginStart="2dp" android:layout_marginEnd="2dp" android:ellipsize="end" - android:maxLines="1" + android:layout_toEndOf="@id/pkg_divider" + android:maxLines="1" /> + <!-- Optional link to app. Only appears if the channel is not disabled and the app +asked for it --> + <ImageButton + android:id="@+id/app_settings" + android:layout_width="@dimen/notification_importance_toggle_size" + android:layout_height="@dimen/notification_importance_toggle_size" android:layout_centerVertical="true" - android:layout_toEndOf="@id/pkg_divider" /> - <LinearLayout - android:id="@+id/info_and_settings" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:visibility="gone" + android:background="@drawable/ripple_drawable" + android:contentDescription="@string/notification_app_settings" + android:src="@drawable/ic_info" + android:layout_toStartOf="@id/info" + android:tint="@color/notification_guts_link_icon_tint"/> + <ImageButton + android:id="@+id/info" + android:layout_width="@dimen/notification_importance_toggle_size" + android:layout_height="@dimen/notification_importance_toggle_size" android:layout_centerVertical="true" + android:background="@drawable/ripple_drawable" + android:contentDescription="@string/notification_more_settings" + android:src="@drawable/ic_settings" android:layout_alignParentEnd="true" - - android:orientation="horizontal"> - <!-- Optional link to app. Only appears if the channel is not disabled and the app -asked for it --> - <ImageButton - android:id="@+id/app_settings" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_centerVertical="true" - android:visibility="gone" - android:background="@drawable/ripple_drawable" - android:contentDescription="@string/notification_app_settings" - android:src="@drawable/ic_info" - android:tint="@color/notification_guts_link_icon_tint" /> - <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins --> - <ImageButton - android:id="@+id/info" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_centerVertical="true" - android:background="@drawable/ripple_drawable" - android:contentDescription="@string/notification_more_settings" - android:src="@drawable/ic_settings" - android:tint="@color/notification_guts_link_icon_tint" /> - </LinearLayout> + android:tint="@color/notification_guts_link_icon_tint"/> </RelativeLayout> <!-- Channel Info Block --> @@ -111,40 +104,22 @@ asked for it --> android:layout_height="wrap_content" android:layout_marginBottom="@dimen/notification_guts_button_spacing" android:paddingEnd="@*android:dimen/notification_content_margin_end" + android:gravity="center" android:orientation="vertical"> - <RelativeLayout - android:id="@+id/names" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView - android:id="@+id/group_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationInfo.Primary" - android:layout_marginStart="2dp" - android:layout_marginEnd="2dp" - android:ellipsize="end" - android:maxLines="1" - android:layout_centerVertical="true" /> - <TextView - android:id="@+id/pkg_group_divider" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationInfo.Primary" - android:layout_marginStart="2dp" - android:layout_marginEnd="2dp" - android:text="@*android:string/notification_header_divider_symbol" - android:layout_centerVertical="true" - android:layout_toEndOf="@id/group_name" /> - <!-- Channel Name --> - <TextView - android:id="@+id/channel_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - style="@style/TextAppearance.NotificationInfo.Primary" - android:layout_toEndOf="@id/pkg_group_divider"/> - </RelativeLayout> + <!-- Channel Name --> + <TextView + android:id="@+id/channel_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + style="@style/TextAppearance.NotificationImportanceChannel"/> + <TextView + android:id="@+id/group_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/TextAppearance.NotificationImportanceChannelGroup" + android:ellipsize="end" + android:maxLines="1"/> </LinearLayout> <LinearLayout @@ -212,8 +187,8 @@ asked for it --> android:id="@+id/inline_controls" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/notification_guts_button_spacing" android:paddingEnd="@*android:dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_guts_option_vertical_padding" android:clipChildren="false" android:clipToPadding="false" android:orientation="vertical"> @@ -225,7 +200,6 @@ asked for it --> android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_guts_option_vertical_padding" style="@*android:style/TextAppearance.DeviceDefault.Notification" /> <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings--> @@ -235,7 +209,6 @@ asked for it --> android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_guts_option_vertical_padding" style="@*android:style/TextAppearance.DeviceDefault.Notification" /> <LinearLayout @@ -243,105 +216,68 @@ asked for it --> android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <!-- Interruptive row --> + <LinearLayout - android:id="@+id/alert_row" + android:id="@+id/buttons" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_guts_option_vertical_padding" - android:paddingBottom="@dimen/notification_guts_option_vertical_padding" - android:paddingStart="@dimen/notification_guts_option_horizontal_padding" - android:orientation="horizontal"> + android:orientation="horizontal" + android:gravity="center"> - <ImageView - android:id="@+id/int_alert" - android:src="@drawable/ic_notification_interruptive" - android:background="@android:color/transparent" - android:layout_gravity="center" + <Button + android:id="@+id/alert" + android:minWidth="@dimen/notification_importance_button_width" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:contentDescription="@string/inline_silent_button_alert"/> + android:minHeight="@dimen/notification_importance_toggle_size" + android:paddingStart="@dimen/notification_importance_button_horiz_padding" + android:paddingEnd="@dimen/notification_importance_button_horiz_padding" + android:drawablePadding="@dimen/notification_importance_drawable_padding" + android:foreground="@drawable/button_ripple_radius" + android:drawableLeft="@drawable/ic_notification_interruptive" + android:text="@string/notification_alert_title" /> - <LinearLayout - android:layout_width="match_parent" + <Button + android:id="@+id/silence" + android:minWidth="@dimen/notification_importance_button_width" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingStart="@dimen/notification_guts_option_horizontal_padding" - android:paddingEnd="@dimen/notification_guts_option_horizontal_padding" - android:orientation="vertical"> - <TextView - android:id="@+id/int_alert_label" - android:text="@string/inline_silent_button_alert" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationInfo.Primary"/> - <TextView - android:id="@+id/int_alert_summary" - android:text="@string/hint_text_alert" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="end" - style="@style/TextAppearance.NotificationInfo.Secondary"/> - </LinearLayout> + android:minHeight="@dimen/notification_importance_toggle_size" + android:paddingStart="@dimen/notification_importance_button_horiz_padding" + android:paddingEnd="@dimen/notification_importance_button_horiz_padding" + android:drawablePadding="@dimen/notification_importance_drawable_padding" + android:foreground="@drawable/button_ripple_radius" + android:layout_marginStart="@dimen/notification_importance_button_separation" + android:drawableLeft="@drawable/ic_notification_gentle" + android:text="@string/notification_silence_title" /> </LinearLayout> - <!-- Gentle row --> - <LinearLayout - android:id="@+id/silent_row" + <TextView + android:id="@+id/description" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_guts_option_vertical_padding" - android:paddingBottom="@dimen/notification_guts_option_vertical_padding" - android:paddingStart="@dimen/notification_guts_option_horizontal_padding" - android:layout_marginTop="@dimen/notification_guts_option_vertical_margin" - android:orientation="horizontal"> - <ImageView - android:id="@+id/int_silent" - android:src="@drawable/ic_notification_gentle" - android:layout_gravity="center" - android:layout_width="36dp" - android:layout_height="36dp" - android:background="@android:color/transparent" - android:contentDescription="@string/inline_silent_button_silent"/> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingStart="@dimen/notification_guts_option_horizontal_padding" - android:paddingEnd="@dimen/notification_guts_option_horizontal_padding"> - <TextView - android:id="@+id/int_silent_label" - android:text="@string/inline_silent_button_silent" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationInfo.Primary"/> - <TextView - android:id="@+id/int_silent_summary" - android:text="@string/hint_text_silent" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="end" - style="@style/TextAppearance.NotificationInfo.Secondary"/> - </LinearLayout> - </LinearLayout> + android:text="@string/notification_alert_title" + android:gravity="center" + android:layout_marginTop="@dimen/notification_importance_text_marginTop" + android:paddingStart="@dimen/notification_importance_description_padding" + android:paddingEnd="@dimen/notification_importance_description_padding" + android:textAppearance="@style/TextAppearance.NotificationImportanceDetail" /> </LinearLayout> <RelativeLayout android:id="@+id/bottom_buttons" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_guts_button_spacing" - android:paddingBottom="@dimen/notification_guts_button_spacing"> + android:paddingTop="@dimen/notification_guts_button_spacing" > <TextView android:id="@+id/turn_off_notifications" android:text="@string/inline_turn_off_notifications" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerVertical="true" android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:minWidth="@dimen/notification_importance_toggle_size" + android:minHeight="@dimen/notification_importance_toggle_size" android:maxWidth="200dp" style="@style/TextAppearance.NotificationInfo.Button"/> <TextView @@ -351,6 +287,8 @@ asked for it --> android:layout_height="wrap_content" android:layout_centerVertical="true" android:maxWidth="125dp" + android:minWidth="@dimen/notification_importance_toggle_size" + android:minHeight="@dimen/notification_importance_toggle_size" android:layout_alignParentEnd="true" style="@style/TextAppearance.NotificationInfo.Button"/> </RelativeLayout> @@ -376,6 +314,8 @@ asked for it --> android:id="@+id/undo" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:minWidth="@dimen/notification_importance_toggle_size" + android:minHeight="@dimen/notification_importance_toggle_size" android:layout_marginTop="@dimen/notification_guts_button_spacing" android:layout_marginBottom="@dimen/notification_guts_button_spacing" android:layout_marginStart="@dimen/notification_guts_button_side_margin" diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 79e2dfb2a40d..83fad66454f3 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -34,7 +34,7 @@ android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="start" + android:layout_gravity="start|center_vertical" android:gravity="center_vertical" android:singleLine="true" android:textAppearance="@style/TextAppearance.QS.Status" diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml index 37c6d9f2d973..5a33f82af763 100644 --- a/packages/SystemUI/res/layout/quick_settings_header_info.xml +++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml @@ -22,17 +22,6 @@ android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end"> - <TextView - android:id="@+id/long_press_tooltip" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="start|center_vertical" - android:alpha="0" - android:text="@string/quick_settings_header_onboarding_text" - android:textAppearance="@style/TextAppearance.QS.TileLabel" - android:textColor="?android:attr/textColorSecondary" - android:visibility="invisible" /> - <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" @@ -45,8 +34,7 @@ android:layout_height="match_parent" android:layout_gravity="start|center_vertical" android:layout_weight="1" - android:gravity="center_vertical" - android:alpha="0" > + android:gravity="center_vertical" > <LinearLayout android:id = "@+id/alarm_container" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 8a0aaea77d52..f9bf47bb7d08 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -45,6 +45,8 @@ <color name="notification_guts_selection_bg">#202124</color> <color name="notification_guts_selection_border">#669DF6</color> <color name="notification_guts_link_icon_tint">@color/GM2_grey_200</color> + <color name="notification_guts_sub_text_color">@color/GM2_grey_200</color> + <color name="notification_guts_header_text_color">@color/GM2_grey_100</color> <!-- The color of the background in the top part of QSCustomizer --> <color name="qs_customize_background">@color/GM2_grey_900</color> diff --git a/packages/SystemUI/res/values-sw900dp-land/dimen.xml b/packages/SystemUI/res/values-sw900dp-land/dimen.xml index ac7e6b815666..1e0600ed5fe0 100644 --- a/packages/SystemUI/res/values-sw900dp-land/dimen.xml +++ b/packages/SystemUI/res/values-sw900dp-land/dimen.xml @@ -19,8 +19,5 @@ <!-- Standard notification width + gravity for tablet large screen device --> <dimen name="notification_panel_width">544dp</dimen> - <!-- Maximum width of quick quick settings panel. --> - <dimen name="qs_quick_layout_width">478dp</dimen> - </resources> diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml index 2c6645480abf..017ca6987820 100644 --- a/packages/SystemUI/res/values-w550dp-land/dimens.xml +++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml @@ -19,6 +19,4 @@ <!-- Standard notification width + gravity --> <dimen name="notification_panel_width">544dp</dimen> - <!-- Maximum width of quick quick settings panel. --> - <dimen name="qs_quick_layout_width">478dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index b2a507549cce..d2a005fc55df 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -90,7 +90,11 @@ <color name="notification_guts_selection_bg">#FFFFFF</color> <color name="notification_guts_selection_border">#4285F4</color> - <color name="notification_guts_link_icon_tint">@color/GM2_grey_900</color> + <color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color> + <color name="notification_guts_sub_text_color">@color/GM2_grey_700</color> + <color name="notification_guts_header_text_color">@color/GM2_grey_900</color> + <color name="notification_silence_color">#FF32c1de</color> + <color name="notification_alert_color">#FFF87B2B</color> <color name="assist_orb_color">#ffffff</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7d76160f8242..df6bc20bbad8 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -206,11 +206,26 @@ <dimen name="notification_guts_option_horizontal_padding">15dp</dimen> <!-- The vertical space between items in the alert selections in the inline settings --> - <dimen name="notification_guts_option_vertical_padding">15dp</dimen> + <dimen name="notification_guts_option_vertical_padding">24dp</dimen> <!-- The vertical space between the alert selections in the inline settings --> <dimen name="notification_guts_option_vertical_margin">6dp</dimen> + <dimen name="notification_importance_toggle_size">48dp</dimen> + <dimen name="notification_importance_toggle_marginTop">28dp</dimen> + <dimen name="notification_importance_toggle_marginBottom">28dp</dimen> + <dimen name="notification_importance_text_marginTop">20dp</dimen> + <dimen name="notification_importance_button_separation">16dp</dimen> + <dimen name="notification_importance_button_width">178dp</dimen> + <dimen name="notification_importance_button_horiz_padding">28dp</dimen> + <dimen name="notification_importance_drawable_padding">8dp</dimen> + <dimen name="notification_importance_description_padding">20dp</dimen> + <dimen name="notification_importance_description_text">12sp</dimen> + <dimen name="notification_importance_channel_text">16sp</dimen> + <dimen name="notification_importance_channel_group_text">14sp</dimen> + <dimen name="notification_importance_button_text">16sp</dimen> + <dimen name="rect_button_radius">8dp</dimen> + <!-- The minimum height for the snackbar shown after the snooze option has been chosen. --> <dimen name="snooze_snackbar_min_height">56dp</dimen> @@ -418,8 +433,6 @@ <dimen name="qs_tile_margin_top">18dp</dimen> <dimen name="qs_tile_background_size">44dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> - <!-- Maximum width of quick quick settings panel. Defaults to MATCH_PARENT--> - <dimen name="qs_quick_layout_width">-1px</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> <dimen name="qs_header_gear_translation">16dp</dimen> <dimen name="qs_header_tile_margin_horizontal">4dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 0b0822cd831e..a2039d07ad59 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -659,6 +659,9 @@ <!-- Accessibility text describing the presence of active location requests by one or more apps --> <string name="accessibility_location_active">Location requests active</string> + <!-- Accessibility text describing sensors off active. [CHAR LIMIT=NONE] --> + <string name="accessibility_sensors_off_active">Sensors off active</string> + <!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_clear_all">Clear all notifications.</string> @@ -1648,17 +1651,29 @@ <!-- Notification Inline controls: continue receiving notifications prompt, app level --> <string name="inline_keep_showing_app">Keep showing notifications from this app?</string> - <!-- Hint text for block button in the interruptiveness settings [CHAR_LIMIT=NONE]--> - <string name="hint_text_block">Blocked notifications do not appear anywhere or play a sound. You can unblock notifications in settings.</string> + <!-- [CHAR LIMIT=100] Notification Importance title --> + <string name="notification_silence_title">Gentle</string> + + <!-- [CHAR LIMIT=100] Notification Importance title --> + <string name="notification_alert_title">Prioritized</string> + + <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> + <string name="notification_channel_summary_low">Always silent. Displays in pull-down shade.</string> + + <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> + <string name="notification_channel_summary_low_status">Always silent. Displays in pull-down shade & status bar.</string> + + <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> + <string name="notification_channel_summary_low_lock">Always silent. Displays in pull-down shade & on lock screen.</string> - <!-- Hint text for silent button in the interruptiveness settings [CHAR_LIMIT=NONE]--> - <string name="hint_text_silent">Silent notifications appear in the shade, but do not appear on the lock screen, present a banner, or play a sound.</string> + <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> + <string name="notification_channel_summary_low_status_lock">Always silent. Displays in pull-down shade, status bar & on lock screen.</string> - <!-- Hint text for alert button in the interruptiveness settings [CHAR_LIMIT=NONE]--> - <string name="hint_text_alert">These notifications will make a sound and show in the notification drawer, status bar, and lock screen</string> + <!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary --> + <string name="notification_channel_summary_default">Makes sound and displays in pull-down shade, status bar & on lock screen.</string> <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> - <string name="notification_unblockable_desc">These notifications can\'t be turned off</string> + <string name="notification_unblockable_desc">These notifications can\'t be modified.</string> <!-- Notification: Control panel: label that displays when viewing settings for a group of notifications posted to multiple channels. --> <string name="notification_multichannel_desc">This group of notifications cannot be configured here</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index eb68b0f0b630..9b471c9c17dc 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -426,55 +426,69 @@ <style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase"> </style> + <style name="TextAppearance.NotificationInfo.Confirmation"> + <item name="android:textSize">14sp</item> + <item name="android:alpha">0.87</item> + </style> + <style name="TextAppearance.NotificationInfo"> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> <item name="android:textColor">@color/notification_primary_text_color</item> </style> - <style name="TextAppearance.NotificationInfo.Primary"> + <style name="TextAppearance.NotificationInfo.Secondary"> + <item name="android:textSize">14sp</item> + <item name="android:alpha">0.54</item> + </style> + + <style name="TextAppearance.NotificationInfo.Button"> <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> <item name="android:textSize">16sp</item> - <item name="android:alpha">0.87</item> + <item name="android:textColor">?android:attr/colorAccent</item> + <item name="android:background">@drawable/btn_borderless_rect</item> + <item name="android:gravity">center_vertical</item> + <item name="android:focusable">true</item> </style> - <style name="TextAppearance.NotificationInfo.Confirmation"> - <item name="android:textSize">14sp</item> - <item name="android:alpha">0.87</item> + <style name="TextAppearance.NotificationImportanceChannel"> + <item name="android:textSize">@dimen/notification_importance_channel_text</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> + <item name="android:textColor">@color/notification_guts_header_text_color</item> + <item name="android:textSize">@dimen/notification_importance_channel_text</item> </style> - <style name="TextAppearance.NotificationInfo.Secondary"> - <item name="android:textSize">14sp</item> - <item name="android:alpha">0.54</item> + <style name="TextAppearance.NotificationImportanceChannelGroup"> + <item name="android:textSize">@dimen/notification_importance_channel_group_text</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + <item name="android:textColor">@color/notification_guts_sub_text_color</item> + <item name="android:textSize">@dimen/notification_importance_channel_group_text</item> </style> - <style name="TextAppearance.NotificationInfo.ButtonLabel"> - <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> - <item name="android:textSize">14sp</item> - <item name="android:alpha">0.54</item> - <item name="android:paddingTop">4dp</item> - <item name="android:paddingBottom">16dp</item> + <style name="TextAppearance.NotificationImportanceHeader"> + <item name="android:textSize">@dimen/notification_importance_description_text</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + <item name="android:textColor">@color/notification_guts_header_text_color</item> </style> - <style name="TextAppearance.NotificationInfo.HintText"> - <item name="android:textSize">12sp</item> - <item name="android:alpha">0.54</item> + <style name="TextAppearance.NotificationImportanceDetail"> + <item name="android:textSize">@dimen/notification_importance_description_text</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + <item name="android:textColor">@color/notification_guts_sub_text_color</item> + <item name="android:gravity">center</item> </style> - <style name="TextAppearance.NotificationInfo.Secondary.Warning"> - <item name="android:textColor">?android:attr/colorError</item> + <style name="TextAppearance.NotificationImportanceButton"> + <item name="android:textSize">@dimen/notification_importance_button_text</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> + <item name="android:gravity">center</item> </style> - <style name="TextAppearance.NotificationInfo.Secondary.Link"> + <style name="TextAppearance.NotificationImportanceButton.Selected" parent="TextAppearance.NotificationImportanceButton"> <item name="android:textColor">?android:attr/colorAccent</item> </style> - <style name="TextAppearance.NotificationInfo.Button"> - <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> - <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/colorAccent</item> - <item name="android:background">@drawable/btn_borderless_rect</item> - <item name="android:gravity">center</item> - <item name="android:focusable">true</item> + <style name="TextAppearance.NotificationImportanceButton.Unselected" parent="TextAppearance.NotificationImportanceButton"> + <item name="android:textColor">?android:attr/textColorPrimary</item> </style> <style name="TextAppearance.HeadsUpStatusBarText" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index 39da19423ee5..9dd5bb46ff0a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -19,6 +19,9 @@ package com.android.systemui.shared.system; import android.graphics.HardwareRenderer; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.Message; import android.view.Surface; import android.view.View; import android.view.ViewRootImpl; @@ -33,8 +36,15 @@ import java.util.function.Consumer; */ public class SyncRtSurfaceTransactionApplierCompat { + private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0; + private final Surface mTargetSurface; private final ViewRootImpl mTargetViewRootImpl; + private final Handler mApplyHandler; + + private int mSequenceNumber = 0; + private int mPendingSequenceNumber = 0; + private Runnable mAfterApplyCallback; /** * @param targetView The view in the surface that acts as synchronization anchor. @@ -42,6 +52,26 @@ public class SyncRtSurfaceTransactionApplierCompat { public SyncRtSurfaceTransactionApplierCompat(View targetView) { mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null; + + mApplyHandler = new Handler(new Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) { + onApplyMessage(msg.arg1); + return true; + } + return false; + } + }); + } + + private void onApplyMessage(int seqNo) { + mSequenceNumber = seqNo; + if (mSequenceNumber == mPendingSequenceNumber && mAfterApplyCallback != null) { + Runnable r = mAfterApplyCallback; + mAfterApplyCallback = null; + r.run(); + } } /** @@ -54,10 +84,15 @@ public class SyncRtSurfaceTransactionApplierCompat { if (mTargetViewRootImpl == null || mTargetViewRootImpl.getView() == null) { return; } + + mPendingSequenceNumber++; + final int toApplySeqNo = mPendingSequenceNumber; mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() { @Override public void onFrameDraw(long frame) { if (mTargetSurface == null || !mTargetSurface.isValid()) { + Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) + .sendToTarget(); return; } TransactionCompat t = new TransactionCompat(); @@ -70,6 +105,7 @@ public class SyncRtSurfaceTransactionApplierCompat { } t.setEarlyWakeup(); t.apply(); + mApplyHandler.sendEmptyMessage(toApplySeqNo); } }); @@ -77,6 +113,28 @@ public class SyncRtSurfaceTransactionApplierCompat { mTargetViewRootImpl.getView().invalidate(); } + /** + * Calls the runnable when any pending apply calls have completed + */ + public void addAfterApplyCallback(final Runnable afterApplyCallback) { + if (mSequenceNumber == mPendingSequenceNumber) { + afterApplyCallback.run(); + } else { + if (mAfterApplyCallback == null) { + mAfterApplyCallback = afterApplyCallback; + } else { + final Runnable oldCallback = mAfterApplyCallback; + mAfterApplyCallback = new Runnable() { + @Override + public void run() { + afterApplyCallback.run(); + oldCallback.run(); + } + }; + } + } + } + public static void applyParams(TransactionCompat t, SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) { t.setMatrix(params.surface, params.matrix); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index fbb30d23c05f..20de4d10692d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -3,7 +3,6 @@ package com.android.keyguard; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.app.WallpaperManager; @@ -12,6 +11,7 @@ import android.graphics.Paint; import android.graphics.Paint.Style; import android.transition.ChangeBounds; import android.transition.Transition; +import android.transition.TransitionListenerAdapter; import android.transition.TransitionManager; import android.transition.TransitionValues; import android.util.AttributeSet; @@ -68,6 +68,11 @@ public class KeyguardClockSwitch extends RelativeLayout { private final Transition mTransition; /** + * Listener for layout transitions. + */ + private final Transition.TransitionListener mTransitionListener; + + /** * Optional/alternative clock injected via plugin. */ private ClockPlugin mClockPlugin; @@ -78,6 +83,12 @@ public class KeyguardClockSwitch extends RelativeLayout { private TextClock mClockView; /** + * Default clock, bold version. + * Used to transition to bold when shrinking the default clock. + */ + private TextClock mClockViewBold; + + /** * Frame for default and custom clock. */ private FrameLayout mSmallClockFrame; @@ -142,6 +153,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mSysuiColorExtractor = colorExtractor; mClockManager = clockManager; mTransition = new ClockBoundsTransition(); + mTransitionListener = new ClockBoundsTransitionListener(); } /** @@ -155,6 +167,7 @@ public class KeyguardClockSwitch extends RelativeLayout { protected void onFinishInflate() { super.onFinishInflate(); mClockView = findViewById(R.id.default_clock_view); + mClockViewBold = findViewById(R.id.default_clock_view_bold); mSmallClockFrame = findViewById(R.id.clock_view); mKeyguardStatusArea = findViewById(R.id.keyguard_status_area); } @@ -165,6 +178,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockManager.addOnClockChangedListener(mClockChangedListener); mStatusBarStateController.addCallback(mStateListener); mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener); + mTransition.addListener(mTransitionListener); updateColors(); } @@ -174,6 +188,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockManager.removeOnClockChangedListener(mClockChangedListener); mStatusBarStateController.removeCallback(mStateListener); mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener); + mTransition.removeListener(mTransitionListener); setClockPlugin(null); } @@ -193,6 +208,7 @@ public class KeyguardClockSwitch extends RelativeLayout { } if (plugin == null) { mClockView.setVisibility(View.VISIBLE); + mClockViewBold.setVisibility(View.INVISIBLE); mKeyguardStatusArea.setVisibility(View.VISIBLE); return; } @@ -203,6 +219,7 @@ public class KeyguardClockSwitch extends RelativeLayout { new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mClockView.setVisibility(View.GONE); + mClockViewBold.setVisibility(View.GONE); } View bigClockView = plugin.getBigClockView(); if (bigClockView != null && mBigClockContainer != null) { @@ -242,6 +259,7 @@ public class KeyguardClockSwitch extends RelativeLayout { */ public void setStyle(Style style) { mClockView.getPaint().setStyle(style); + mClockViewBold.getPaint().setStyle(style); if (mClockPlugin != null) { mClockPlugin.setStyle(style); } @@ -252,6 +270,7 @@ public class KeyguardClockSwitch extends RelativeLayout { */ public void setTextColor(int color) { mClockView.setTextColor(color); + mClockViewBold.setTextColor(color); if (mClockPlugin != null) { mClockPlugin.setTextColor(color); } @@ -259,18 +278,22 @@ public class KeyguardClockSwitch extends RelativeLayout { public void setShowCurrentUserTime(boolean showCurrentUserTime) { mClockView.setShowCurrentUserTime(showCurrentUserTime); + mClockViewBold.setShowCurrentUserTime(showCurrentUserTime); } public void setTextSize(int unit, float size) { mClockView.setTextSize(unit, size); + mClockViewBold.setTextSize(unit, size); } public void setFormat12Hour(CharSequence format) { mClockView.setFormat12Hour(format); + mClockViewBold.setFormat12Hour(format); } public void setFormat24Hour(CharSequence format) { mClockView.setFormat24Hour(format); + mClockViewBold.setFormat24Hour(format); } /** @@ -316,6 +339,7 @@ public class KeyguardClockSwitch extends RelativeLayout { */ public void refresh() { mClockView.refresh(); + mClockViewBold.refresh(); if (mClockPlugin != null) { mClockPlugin.onTimeTick(); } @@ -356,8 +380,7 @@ public class KeyguardClockSwitch extends RelativeLayout { /** * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in - * these - * cases. + * these cases. */ public void setKeyguardShowingHeader(boolean hasHeader) { if (mShowingHeader == hasHeader || hasCustomClock()) { @@ -371,8 +394,11 @@ public class KeyguardClockSwitch extends RelativeLayout { int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding); mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); + mClockViewBold.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(), mClockView.getPaddingRight(), paddingBottom); + mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(), + mClockViewBold.getPaddingRight(), paddingBottom); } @VisibleForTesting(otherwise = VisibleForTesting.NONE) @@ -389,6 +415,7 @@ public class KeyguardClockSwitch extends RelativeLayout { pw.println("KeyguardClockSwitch:"); pw.println(" mClockPlugin: " + mClockPlugin); pw.println(" mClockView: " + mClockView); + pw.println(" mClockViewBold: " + mClockViewBold); pw.println(" mSmallClockFrame: " + mSmallClockFrame); pw.println(" mBigClockContainer: " + mBigClockContainer); pw.println(" mKeyguardStatusArea: " + mKeyguardStatusArea); @@ -400,11 +427,15 @@ public class KeyguardClockSwitch extends RelativeLayout { /** * Special layout transition that scales the clock view as its bounds change, to make it look - * like - * the text is shrinking. + * like the text is shrinking. */ private class ClockBoundsTransition extends ChangeBounds { + /** + * Animation fraction when text is transitioned to/from bold. + */ + private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f; + ClockBoundsTransition() { setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2); setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); @@ -435,29 +466,51 @@ public class KeyguardClockSwitch extends RelativeLayout { .getDimensionPixelSize(R.dimen.widget_small_font_size); float startScale = mShowingHeader ? bigFontSize / smallFontSize : smallFontSize / bigFontSize; + final int normalViewVisibility = mShowingHeader ? View.INVISIBLE : View.VISIBLE; + final int boldViewVisibility = mShowingHeader ? View.VISIBLE : View.INVISIBLE; + final float boldTransitionFraction = mShowingHeader ? TO_BOLD_TRANSITION_FRACTION : + 1f - TO_BOLD_TRANSITION_FRACTION; boundsAnimator.addUpdateListener(animation -> { + final float fraction = animation.getAnimatedFraction(); + if (fraction > boldTransitionFraction) { + mClockView.setVisibility(normalViewVisibility); + mClockViewBold.setVisibility(boldViewVisibility); + } float scale = MathUtils.lerp(startScale, 1f /* stop */, animation.getAnimatedFraction()); mClockView.setPivotX(mClockView.getWidth() / 2f); + mClockViewBold.setPivotX(mClockViewBold.getWidth() / 2f); mClockView.setPivotY(0); + mClockViewBold.setPivotY(0); mClockView.setScaleX(scale); + mClockViewBold.setScaleX(scale); mClockView.setScaleY(scale); - }); - boundsAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - mClockView.setScaleX(1f); - mClockView.setScaleY(1f); - } - - @Override - public void onAnimationCancel(Animator animator) { - onAnimationEnd(animator); - } + mClockViewBold.setScaleY(scale); }); } return animator; } } + + /** + * Transition listener for layout transition that scales the clock view. + */ + private class ClockBoundsTransitionListener extends TransitionListenerAdapter { + + @Override + public void onTransitionEnd(Transition transition) { + mClockView.setVisibility(mShowingHeader ? View.INVISIBLE : View.VISIBLE); + mClockViewBold.setVisibility(mShowingHeader ? View.VISIBLE : View.INVISIBLE); + mClockView.setScaleX(1f); + mClockViewBold.setScaleX(1f); + mClockView.setScaleY(1f); + mClockViewBold.setScaleY(1f); + } + + @Override + public void onTransitionCancel(Transition transition) { + onTransitionEnd(transition); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java index 922c65e453be..b3fc69e8a49d 100644 --- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java +++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java @@ -59,7 +59,10 @@ public class SliceBroadcastRelayHandler extends SystemUI { } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) { Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI); if (DEBUG) Log.d(TAG, "Unregister " + uri); - getAndRemoveRelay(uri).unregister(mContext); + BroadcastRelay relay = getAndRemoveRelay(uri); + if (relay != null) { + relay.unregister(mContext); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 1bd8e0d2ce0f..4ed28f92d5cb 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -180,6 +180,10 @@ public class AssistManager implements ConfigurationChangedReceiver { ? TIMEOUT_SERVICE : TIMEOUT_ACTIVITY); } + + if (args == null) { + args = new Bundle(); + } args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.uptimeMillis()); startAssistInternal(args, assistComponent, isService); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 744f88d19ba3..ef383add644e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -32,7 +32,6 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; -import android.app.INotificationManager; import android.app.Notification; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -52,6 +51,7 @@ import androidx.annotation.IntDef; import androidx.annotation.MainThread; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -131,8 +131,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private StatusBarStateListener mStatusBarStateListener; private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; - - private INotificationManager mNotificationManagerService; + private IStatusBarService mBarService; // Used for determining view rect for touch interaction private Rect mTempRect = new Rect(); @@ -207,13 +206,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mNotificationEntryManager.addNotificationEntryListener(mEntryListener); - try { - mNotificationManagerService = INotificationManager.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE)); - } catch (ServiceManager.ServiceNotFoundException e) { - e.printStackTrace(); - } - mStatusBarWindowController = statusBarWindowController; mStatusBarStateListener = new StatusBarStateListener(); Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener); @@ -231,6 +223,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleData = data; mBubbleData.setListener(mBubbleDataListener); mSurfaceSynchronizer = synchronizer; + + mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } /** @@ -462,6 +457,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (mStackView != null) { mStackView.removeBubble(bubble); } + if (!bubble.entry.showInShadeWhenBubble()) { + // The notification is gone & bubble is gone, time to actually remove it + mNotificationEntryManager.performRemoveNotification(bubble.entry.notification); + } else { + // The notification is still in the shade but we've removed the bubble so + // lets make sure NoMan knows it's not a bubble anymore + try { + mBarService.onNotificationBubbleChanged(bubble.getKey(), false /* isBubble */); + } catch (RemoteException e) { + // Bad things have happened + } + } } public void onBubbleUpdated(Bubble bubble) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index a4a0fe18f5cb..d9fe47a94d5f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -717,12 +717,10 @@ public class BubbleStackView extends FrameLayout { /** Moves the bubbles out of the way if they're going to be over the keyboard. */ public void onImeVisibilityChanged(boolean visible, int height) { + mStackAnimationController.setImeHeight(height + mImeOffset); + if (!mIsExpanded) { - if (visible) { - mStackAnimationController.updateBoundsForVisibleImeAndAnimate(height + mImeOffset); - } else { - mStackAnimationController.updateBoundsForInvisibleImeAndAnimate(); - } + mStackAnimationController.animateForImeVisibility(visible); } } @@ -787,6 +785,10 @@ public class BubbleStackView extends FrameLayout { StatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED); } + void onDragFinishAsDismiss() { + mIsDragging = false; + } + /** * Calculates how large the expanded view of the bubble can be. This takes into account the * y position when the bubbles are expanded as well as the bounds of the dismiss target. @@ -826,9 +828,12 @@ public class BubbleStackView extends FrameLayout { if (updateMessage != null && !isExpanded() && !mIsExpansionAnimating && !mIsDragging) { final PointF stackPos = mStackAnimationController.getStackPosition(); + mFlyout.setAlpha(0f); + mFlyout.setVisibility(VISIBLE); + mFlyoutText.setText(updateMessage); mFlyout.measure(WRAP_CONTENT, WRAP_CONTENT); - mFlyout.post(() -> { + post(() -> { final boolean onLeft = mStackAnimationController.isStackOnLeftSide(); final float destinationX = onLeft ? stackPos.x + mBubbleSize + mBubblePadding @@ -837,9 +842,6 @@ public class BubbleStackView extends FrameLayout { // Translate towards the stack slightly, then spring out from the stack. mFlyout.setTranslationX(destinationX + (onLeft ? -mBubblePadding : mBubblePadding)); mFlyout.setTranslationY(stackPos.y); - mFlyout.setAlpha(0f); - - mFlyout.setVisibility(VISIBLE); mFlyout.animate().alpha(1f); mFlyoutSpring.animateToFinalPosition(destinationX); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index baeedaacdd95..a51d46c0a848 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -148,6 +148,7 @@ class BubbleTouchHandler implements View.OnTouchListener { trackMovement(event); if (mInDismissTarget && isStack) { mController.dismissStack(BubbleController.DISMISS_USER_GESTURE); + mStack.onDragFinishAsDismiss(); } else if (isFlyout) { // TODO(b/129768381): Expand if tapped, dismiss if swiped away. if (!mStack.isExpanded() && !mMovedEnough) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 74a6b6005450..eb6ac796612a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -334,41 +334,38 @@ public class StackAnimationController extends mLayout.removeEndActionForProperty(DynamicAnimation.TRANSLATION_Y); } - /** - * Save the IME height so that the allowable stack bounds reflect the now-visible IME, and - * animate the stack out of the way if necessary. - */ - public void updateBoundsForVisibleImeAndAnimate(int imeHeight) { + /** Save the current IME height so that we know where the stack bounds should be. */ + public void setImeHeight(int imeHeight) { mImeHeight = imeHeight; - - final float maxBubbleY = getAllowableStackPositionRegion().bottom; - if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) { - mPreImeY = mStackPosition.y; - - springFirstBubbleWithStackFollowing( - DynamicAnimation.TRANSLATION_Y, - getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null) - .setStiffness(SpringForce.STIFFNESS_LOW), - /* startVel */ 0f, - maxBubbleY); - } } /** - * Clear the IME height from the bounds and animate the stack back to its original position, - * assuming it wasn't moved in the meantime. + * Animates the stack either away from the newly visible IME, or back to its original position + * due to the IME going away. */ - public void updateBoundsForInvisibleImeAndAnimate() { - mImeHeight = 0; + public void animateForImeVisibility(boolean imeVisible) { + final float maxBubbleY = getAllowableStackPositionRegion().bottom; + float destinationY = Float.MIN_VALUE; - if (mPreImeY > Float.MIN_VALUE) { + if (imeVisible) { + if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) { + mPreImeY = mStackPosition.y; + destinationY = maxBubbleY; + } + } else { + if (mPreImeY > Float.MIN_VALUE) { + destinationY = mPreImeY; + mPreImeY = Float.MIN_VALUE; + } + } + + if (destinationY > Float.MIN_VALUE) { springFirstBubbleWithStackFollowing( DynamicAnimation.TRANSLATION_Y, getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null) - .setStiffness(SpringForce.STIFFNESS_LOW), + .setStiffness(SpringForce.STIFFNESS_LOW), /* startVel */ 0f, - mPreImeY); - mPreImeY = Float.MIN_VALUE; + destinationY); } } @@ -538,6 +535,7 @@ public class StackAnimationController extends Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y)); mStackPosition.set(pos.x, pos.y); + mLayout.cancelAllAnimations(); cancelStackPositionAnimations(); // Since we're not using the chained animations, apply the offsets manually. diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 0fe6611b4274..a381e7b60f0a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -247,6 +247,10 @@ public class DozeTriggers implements DozeMachine.Part { } if (state == DozeMachine.State.DOZE) { mMachine.requestState(DozeMachine.State.DOZE_AOD); + // Logs AOD open due to sensor wake up. + mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) + .setType(MetricsEvent.TYPE_OPEN) + .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP)); } }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP); } else { @@ -254,6 +258,10 @@ public class DozeTriggers implements DozeMachine.Part { boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); if (!pausing && !paused) { mMachine.requestState(DozeMachine.State.DOZE); + // Logs AOD close due to sensor wake up. + mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) + .setType(MetricsEvent.TYPE_CLOSE) + .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 505957aecb6e..e128531cf49d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1588,6 +1588,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mBackgroundDrawable = mContext.getDrawable( com.android.systemui.R.drawable.global_action_panel_scrim); mScrimAlpha = 1f; + initializePanel(); } mGlobalActionsLayout.setSnapToEdge(true); getWindow().setBackgroundDrawable(mBackgroundDrawable); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java index 60dceef23f7c..84f7e89d9f2f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java @@ -170,6 +170,11 @@ public class PipAccessibilityInteractionConnection // We should not be here. } + @Override + public void notifyOutsideTouch() { + // Do nothing. + } + public static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() { AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java index f19445c1f33b..a1a7566c8709 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java @@ -132,15 +132,18 @@ public class QSCarrier extends LinearLayout { } @Override + protected void onFinishInflate() { + setSelected(true); + } + + @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); // Only show marquee when visible if (visibility == VISIBLE) { setEllipsize(TextUtils.TruncateAt.MARQUEE); - setSelected(true); } else { setEllipsize(TextUtils.TruncateAt.END); - setSelected(false); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index fcaf98165016..89aa96db7a92 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -17,6 +17,7 @@ package com.android.systemui.qs; import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState; +import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.annotation.Nullable; import android.content.ComponentName; @@ -37,6 +38,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; import com.android.systemui.Dependency; +import com.android.systemui.DumpController; +import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile; @@ -51,11 +54,17 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController.Brightne import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; +import javax.inject.Inject; +import javax.inject.Named; + /** View that represents the quick settings tile panel (when expanded/pulled down). **/ -public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener { +public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener, + Dumpable { public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness"; public static final String QS_SHOW_HEADER = "qs_show_header"; @@ -74,6 +83,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private QSDetail.Callback mCallback; private BrightnessController mBrightnessController; + private DumpController mDumpController; protected QSTileHost mHost; protected QSSecurityFooter mFooter; @@ -93,6 +103,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } public QSPanel(Context context, AttributeSet attrs) { + this(context, attrs, null); + } + + @Inject + public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, + DumpController dumpController) { super(context, attrs); mContext = context; @@ -119,6 +135,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mBrightnessController = new BrightnessController(getContext(), findViewById(R.id.brightness_slider)); + mDumpController = dumpController; } protected void addDivider() { @@ -170,6 +187,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mBrightnessMirrorController != null) { mBrightnessMirrorController.addCallback(this); } + if (mDumpController != null) mDumpController.addListener(this); } @Override @@ -184,6 +202,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mBrightnessMirrorController != null) { mBrightnessMirrorController.removeCallback(this); } + if (mDumpController != null) mDumpController.removeListener(this); super.onDetachedFromWindow(); } @@ -649,6 +668,18 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(getClass().getSimpleName() + ":"); + pw.println(" Tile records:"); + for (TileRecord record : mRecords) { + if (record.tile instanceof Dumpable) { + pw.print(" "); ((Dumpable) record.tile).dump(fd, pw, args); + pw.print(" "); pw.println(record.tileView.toString()); + } + } + } + protected static class Record { DetailAdapter detailAdapter; int x; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index dfc3e66d8d98..3c4898c7658b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -30,6 +30,8 @@ import android.text.TextUtils; import android.util.Log; import com.android.systemui.Dependency; +import com.android.systemui.DumpController; +import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.PluginListener; @@ -47,6 +49,8 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -61,7 +65,7 @@ import javax.inject.Singleton; /** Platform implementation of the quick settings tile host **/ @Singleton -public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> { +public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable { private static final String TAG = "QSTileHost"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -73,6 +77,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> { private final TileServices mServices; private final TunerService mTunerService; private final PluginManager mPluginManager; + private final DumpController mDumpController; private final List<Callback> mCallbacks = new ArrayList<>(); private AutoTileManager mAutoTiles; @@ -89,17 +94,20 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> { @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper, PluginManager pluginManager, TunerService tunerService, - Provider<AutoTileManager> autoTiles) { + Provider<AutoTileManager> autoTiles, + DumpController dumpController) { mIconController = iconController; mContext = context; mTunerService = tunerService; mPluginManager = pluginManager; + mDumpController = dumpController; mServices = new TileServices(this, bgLooper); defaultFactory.setHost(this); mQsFactories.add(defaultFactory); pluginManager.addPluginListener(this, QSFactory.class, true); + mDumpController.addListener(this); mainHandler.post(() -> { // This is technically a hack to avoid circular dependency of @@ -121,6 +129,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> { mTunerService.removeTunable(this); mServices.destroy(); mPluginManager.removePluginListener(this); + mDumpController.removeListener(this); } @Override @@ -363,4 +372,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> { } return tiles; } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("QSTileHost:"); + mTiles.values().stream().filter(obj -> obj instanceof Dumpable) + .forEach(o -> ((Dumpable) o).dump(fd, pw, args)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 995dc66d8214..73f6fc5ea2f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -16,6 +16,8 @@ package com.android.systemui.qs; +import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; + import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -25,6 +27,7 @@ import android.view.View; import android.widget.LinearLayout; import com.android.systemui.Dependency; +import com.android.systemui.DumpController; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; @@ -36,6 +39,9 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.util.ArrayList; import java.util.Collection; +import javax.inject.Inject; +import javax.inject.Named; + /** * Version of QSPanel that only shows N Quick Tiles in the QS Header. */ @@ -49,8 +55,10 @@ public class QuickQSPanel extends QSPanel { private int mMaxTiles; protected QSPanel mFullPanel; - public QuickQSPanel(Context context, AttributeSet attrs) { - super(context, attrs); + @Inject + public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, + DumpController dumpController) { + super(context, attrs, dumpController); if (mFooter != null) { removeView(mFooter.getView()); } @@ -181,11 +189,16 @@ public class QuickQSPanel extends QSPanel { private static class HeaderTileLayout extends TileLayout { private boolean mListening; + private Rect mClippingBounds = new Rect(); public HeaderTileLayout(Context context) { super(context); setClipChildren(false); setClipToPadding(false); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + setLayoutParams(lp); } @Override @@ -199,13 +212,6 @@ public class QuickQSPanel extends QSPanel { updateResources(); } - private void updateLayoutParams() { - int width = getResources().getDimensionPixelSize(R.dimen.qs_quick_layout_width); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width, LayoutParams.MATCH_PARENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; - setLayoutParams(lp); - } - private LayoutParams generateTileLayoutParams() { LayoutParams lp = new LayoutParams(mCellWidth, mCellHeight); return lp; @@ -219,8 +225,8 @@ public class QuickQSPanel extends QSPanel { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // We only care about clipping on the right side - Rect bounds = new Rect(0, 0, r - l, 10000); - setClipBounds(bounds); + mClippingBounds.set(0, 0, r - l, 10000); + setClipBounds(mClippingBounds); calculateColumns(); @@ -237,8 +243,6 @@ public class QuickQSPanel extends QSPanel { mCellWidth = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size); mCellHeight = mCellWidth; - updateLayoutParams(); - return false; } @@ -252,9 +256,9 @@ public class QuickQSPanel extends QSPanel { } final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd(); - final int leftoverWithespace = availableWidth - maxTiles * mCellWidth; + final int leftoverWhitespace = availableWidth - maxTiles * mCellWidth; final int smallestHorizontalMarginNeeded; - smallestHorizontalMarginNeeded = leftoverWithespace / Math.max(1, maxTiles - 1); + smallestHorizontalMarginNeeded = leftoverWhitespace / Math.max(1, maxTiles - 1); if (smallestHorizontalMarginNeeded > 0){ mCellMarginHorizontal = smallestHorizontalMarginNeeded; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 2e3065aeee03..346ffa2e6ad7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -18,8 +18,6 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.annotation.ColorInt; import android.app.ActivityManager; import android.app.AlarmManager; @@ -39,7 +37,6 @@ import android.provider.Settings; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; import android.util.AttributeSet; -import android.util.Log; import android.util.Pair; import android.util.StatsLog; import android.view.DisplayCutout; @@ -56,7 +53,6 @@ import androidx.annotation.VisibleForTesting; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; -import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; @@ -123,10 +119,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements private View mSystemIconsView; private View mQuickQsStatusIcons; private View mHeaderTextContainerView; - /** View containing the next alarm and ringer mode info. */ - private View mStatusContainer; - /** Tooltip for educating users that they can long press on icons to see more details. */ - private View mLongPressTooltipView; private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; private AlarmManager.AlarmClockInfo mNextAlarm; @@ -146,8 +138,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements private BatteryMeterView mBatteryRemainingIcon; private PrivacyItemController mPrivacyItemController; - /** Counts how many times the long press tooltip has been shown to the user. */ - private int mShownCount; private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() { @Override @@ -159,11 +149,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements private boolean mHasTopCutout = false; private boolean mPrivacyChipLogged = false; - /** - * Runnable for automatically fading out the long press tooltip (as if it were animating away). - */ - private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false); - private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() { @Override public void privacyChanged(List<PrivacyItem> privacyItems) { @@ -183,7 +168,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mStatusBarIconController = statusBarIconController; mActivityStarter = activityStarter; mPrivacyItemController = privacyItemController; - mShownCount = getStoredShownCount(); } @Override @@ -199,10 +183,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements iconContainer.setShouldRestrictIcons(false); mIconManager = new TintedIconManager(iconContainer); - // Views corresponding to the header info section (e.g. tooltip and next alarm). + // Views corresponding to the header info section (e.g. ringer and next alarm). mHeaderTextContainerView = findViewById(R.id.header_text_container); - mLongPressTooltipView = findViewById(R.id.long_press_tooltip); - mStatusContainer = findViewById(R.id.status_container); mStatusSeparator = findViewById(R.id.status_separator); mNextAlarmIcon = findViewById(R.id.next_alarm_icon); mNextAlarmTextView = findViewById(R.id.next_alarm_text); @@ -267,7 +249,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE; mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE : View.GONE); - updateTooltipShow(); } } @@ -351,8 +332,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor); } - - @Override public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); @@ -457,21 +436,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mPrivacyChip.setExpanded(expansionFraction > 0.5); mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction); } - - // Check the original expansion fraction - we don't want to show the tooltip until the - // panel is pulled all the way out. - if (expansionFraction == 1f) { - // QS is fully expanded, bring in the tooltip. - showLongPressTooltip(); - } - } - - /** Returns the latest stored tooltip shown count from SharedPreferences. */ - private int getStoredShownCount() { - return Prefs.getInt( - mContext, - Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, - TOOLTIP_NOT_YET_SHOWN_COUNT); } public void disable(int state1, int state2, boolean animate) { @@ -592,109 +556,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateStatusText(); } - private void updateTooltipShow() { - if (hasStatusText()) { - hideLongPressTooltip(true /* shouldShowStatusText */); - } else { - hideStatusText(); - } - updateHeaderTextContainerAlphaAnimator(); - } - - private boolean hasStatusText() { - return mNextAlarmTextView.getVisibility() == View.VISIBLE - || mRingerModeTextView.getVisibility() == View.VISIBLE; - } - - /** - * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying - * the space). - */ - public void showLongPressTooltip() { - // If we have status text to show, don't bother fading in the tooltip. - if (hasStatusText()) { - return; - } - - if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) { - mLongPressTooltipView.animate().cancel(); - mLongPressTooltipView.setVisibility(View.VISIBLE); - mLongPressTooltipView.animate() - .alpha(1f) - .setDuration(FADE_ANIMATION_DURATION_MS) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mHandler.postDelayed( - mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS); - } - }) - .start(); - - // Increment and drop the shown count in prefs for the next time we're deciding to - // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet - // in prefs (say, from a long press). - if (getStoredShownCount() <= mShownCount) { - Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount); - } - } - } - - /** - * Fades out the long press tooltip if it's partially visible - short circuits any running - * animation. Additionally has the ability to fade in the status info text. - * - * @param shouldShowStatusText whether we should fade in the status text - */ - private void hideLongPressTooltip(boolean shouldShowStatusText) { - mLongPressTooltipView.animate().cancel(); - if (mLongPressTooltipView.getVisibility() == View.VISIBLE - && mLongPressTooltipView.getAlpha() != 0f) { - mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable); - mLongPressTooltipView.animate() - .alpha(0f) - .setDuration(FADE_ANIMATION_DURATION_MS) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip"); - mLongPressTooltipView.setVisibility(View.INVISIBLE); - - if (shouldShowStatusText) { - showStatus(); - } - } - }) - .start(); - } else { - mLongPressTooltipView.setVisibility(View.INVISIBLE); - if (shouldShowStatusText) { - showStatus(); - } - } - } - - /** - * Fades in the updated status text. Note that if there's already a status showing, this will - * immediately fade it out and fade in the updated status. - */ - private void showStatus() { - mStatusContainer.setAlpha(0f); - - mStatusContainer.animate() - .alpha(1f) - .setDuration(FADE_ANIMATION_DURATION_MS) - .start(); - } - - /** Fades out the status text. */ - private void hideStatusText() { - mStatusContainer.animate() - .alpha(0f) - .setDuration(FADE_ANIMATION_DURATION_MS) - .start(); - } - public void updateEverything() { post(() -> setClickable(!mExpanded)); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index 3c59f69fdee6..31526bf8f5e1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -47,6 +47,7 @@ public class QSIconViewImpl extends QSIconView { private boolean mAnimationEnabled = true; private int mState = -1; private int mTint; + private QSTile.Icon mLastIcon; public QSIconViewImpl(Context context) { super(context); @@ -75,6 +76,16 @@ public class QSIconViewImpl extends QSIconView { } @Override + public String toString() { + final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('['); + sb.append("state=" + mState); + sb.append(", tint=" + mTint); + if (mLastIcon != null) sb.append(", lastIcon=" + mLastIcon.toString()); + sb.append("]"); + return sb.toString(); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int w = getMeasuredWidth(); int top = 0; @@ -91,6 +102,7 @@ public class QSIconViewImpl extends QSIconView { if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag)) || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) { boolean shouldAnimate = allowAnimations && shouldAnimate(iv); + mLastIcon = icon; Drawable d = icon != null ? shouldAnimate ? icon.getDrawable(mContext) : icon.getInvisibleDrawable(mContext) : null; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index a732a253f5a3..c186e59056aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -318,6 +318,16 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { } } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('['); + sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")"); + sb.append(", iconView=" + mIcon.toString()); + sb.append(", tileState=" + mTileState); + sb.append("]"); + return sb.toString(); + } + private class H extends Handler { private static final int STATE_CHANGED = 1; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index e275690f70aa..1f857ff7b5ea 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -52,6 +52,7 @@ import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.Utils; import com.android.systemui.Dependency; +import com.android.systemui.Dumpable; import com.android.systemui.Prefs; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; @@ -63,6 +64,8 @@ import com.android.systemui.qs.PagedTileLayout.TilePage; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QuickStatusBarHeader; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; /** @@ -74,7 +77,7 @@ import java.util.ArrayList; * * @param <TState> see above */ -public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner { +public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner, Dumpable { protected final String TAG = "Tile." + getClass().getSimpleName(); protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG); @@ -592,4 +595,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy return context.getDrawable(mAnimatedResId).getConstantState().newDrawable(); } } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(this.getClass().getSimpleName() + ":"); + pw.print(" "); pw.println(getState().toString()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index b2302cccba25..898f64be9e55 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -354,17 +354,22 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { - mHandler.removeCallbacks(mDeferredConnectionCallback); - mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser(); mConnectionBackoffAttempts = 0; - mOverviewProxy = IOverviewProxy.Stub.asInterface(service); - // Listen for launcher's death + mHandler.removeCallbacks(mDeferredConnectionCallback); try { service.linkToDeath(mOverviewServiceDeathRcpt, 0); } catch (RemoteException e) { + // Failed to link to death (process may have died between binding and connecting), + // just unbind the service for now and retry again Log.e(TAG_OPS, "Lost connection to launcher service", e); + disconnectFromLauncherService(); + retryConnectionWithBackoff(); + return; } + mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser(); + mOverviewProxy = IOverviewProxy.Stub.asInterface(service); + Bundle params = new Bundle(); params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); @@ -550,7 +555,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mHandler.post(()-> { StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class); if (bar != null) { - System.out.println("MERONG dispatchNotificationPanelTouchEvent"); mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL); bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent); mStatusBarGestureDownEvent.recycle(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 610159345110..c886062a1b02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -54,7 +54,9 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController.StateList import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -77,6 +79,8 @@ public class KeyguardIndicationController implements StateListener { private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; private final Context mContext; + private final ShadeController mShadeController; + private final AccessibilityController mAccessibilityController; private ViewGroup mIndicationArea; private KeyguardIndicationTextView mTextView; private KeyguardIndicationTextView mDisclosure; @@ -116,7 +120,9 @@ public class KeyguardIndicationController implements StateListener { public KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon) { this(context, indicationArea, lockIcon, new LockPatternUtils(context), - WakeLock.createPartial(context, "Doze:KeyguardIndication")); + WakeLock.createPartial(context, "Doze:KeyguardIndication"), + Dependency.get(ShadeController.class), + Dependency.get(AccessibilityController.class)); registerCallbacks(KeyguardUpdateMonitor.getInstance(context)); } @@ -126,7 +132,8 @@ public class KeyguardIndicationController implements StateListener { */ @VisibleForTesting KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon, - LockPatternUtils lockPatternUtils, WakeLock wakeLock) { + LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController, + AccessibilityController accessibilityController) { mContext = context; mIndicationArea = indicationArea; mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); @@ -134,9 +141,12 @@ public class KeyguardIndicationController implements StateListener { mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE); mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); mLockIcon = lockIcon; + mShadeController = shadeController; + mAccessibilityController = accessibilityController; // lock icon is not used on all form factors. if (mLockIcon != null) { - mLockIcon.setOnLongClickListener(this::handleTrustCircleClick); + mLockIcon.setOnLongClickListener(this::handleLockLongClick); + mLockIcon.setOnClickListener(this::handleLockClick); } mWakeLock = new SettableWakeLock(wakeLock, TAG); mLockPatternUtils = lockPatternUtils; @@ -173,7 +183,7 @@ public class KeyguardIndicationController implements StateListener { Dependency.get(StatusBarStateController.class).removeCallback(this); } - private boolean handleTrustCircleClick(View view) { + private boolean handleLockLongClick(View view) { mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK, 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); showTransientIndication(R.string.keyguard_indication_trust_disabled); @@ -182,6 +192,13 @@ public class KeyguardIndicationController implements StateListener { return true; } + private void handleLockClick(View view) { + if (!mAccessibilityController.isAccessibilityEnabled()) { + return; + } + mShadeController.showBouncer(false /* scrimmed */); + } + /** * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this * {@link KeyguardIndicationController}. @@ -693,6 +710,9 @@ public class KeyguardIndicationController implements StateListener { @Override public void onKeyguardBouncerChanged(boolean bouncer) { + if (mLockIcon == null) { + return; + } mLockIcon.setBouncerVisible(bouncer); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 622b869c9e4c..e6875e4d8dc3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -18,8 +18,7 @@ package com.android.systemui.statusbar.notification.row; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; -import static android.app.NotificationManager.IMPORTANCE_MIN; -import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -37,9 +36,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.graphics.Color; import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; import android.metrics.LogMaker; import android.os.Handler; import android.os.RemoteException; @@ -60,7 +57,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import java.util.List; @@ -104,6 +100,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private NotificationChannel mSingleNotificationChannel; private int mStartingChannelImportance; private boolean mWasShownHighPriority; + private boolean mShowOnLockscreen; + private boolean mShowInStatusBar; /** * The last importance level chosen by the user. Null if the user has not chosen an importance * level; non-null once the user takes an action which indicates an explicit preference. @@ -119,7 +117,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; private NotificationGuts mGutsContainer; - private GradientDrawable mSelectedBackground; + private Drawable mSelectedBackground; + private Drawable mUnselectedBackground; /** Whether this view is being shown as part of the blocking helper. */ private boolean mIsForBlockingHelper; @@ -133,6 +132,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnAlert = v -> { mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; mChosenImportance = IMPORTANCE_DEFAULT; + setImportanceSummary(ACTION_ALERT); updateButtons(ACTION_ALERT); }; @@ -140,6 +140,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnSilent = v -> { mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; mChosenImportance = IMPORTANCE_LOW; + setImportanceSummary(ACTION_TOGGLE_SILENT); updateButtons(ACTION_TOGGLE_SILENT); }; @@ -276,14 +277,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; - mSelectedBackground = new GradientDrawable(); - mSelectedBackground.setShape(GradientDrawable.RECTANGLE); - mSelectedBackground.setColor(mContext.getColor(R.color.notification_guts_selection_bg)); - final float cornerRadii = getResources().getDisplayMetrics().density * 8; - mSelectedBackground.setCornerRadii(new float[]{cornerRadii, cornerRadii, cornerRadii, - cornerRadii, cornerRadii, cornerRadii, cornerRadii, cornerRadii}); - mSelectedBackground.setStroke((int) (getResources().getDisplayMetrics().density * 2), - mContext.getColor(R.color.notification_guts_selection_border)); + mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected); + mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected); int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); @@ -297,6 +292,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G && numTotalChannels == 1; } + mShowInStatusBar = !mINotificationManager.shouldHideSilentStatusIcons( + mContext.getPackageName()); + // TODO: b/128445911 use show on lockscreen setting + bindHeader(); bindChannelDetails(); @@ -334,6 +333,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G findViewById(R.id.non_configurable_text).setVisibility(VISIBLE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); + ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); } else if (mNumUniqueChannelsInRow > 1) { findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); @@ -353,15 +353,17 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G done.setOnClickListener(mOnDismissSettings); - View silent = findViewById(R.id.silent_row); - View alert = findViewById(R.id.alert_row); + View silent = findViewById(R.id.silence); + View alert = findViewById(R.id.alert); silent.setOnClickListener(mOnSilent); alert.setOnClickListener(mOnAlert); if (mWasShownHighPriority) { updateButtons(ACTION_ALERT); + setImportanceSummary(ACTION_ALERT); } else { updateButtons(ACTION_TOGGLE_SILENT); + setImportanceSummary(ACTION_TOGGLE_SILENT); } } @@ -482,14 +484,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } TextView groupNameView = findViewById(R.id.group_name); - TextView groupDividerView = findViewById(R.id.pkg_group_divider); if (groupName != null) { groupNameView.setText(groupName); groupNameView.setVisibility(View.VISIBLE); - groupDividerView.setVisibility(View.VISIBLE); } else { groupNameView.setVisibility(View.GONE); - groupDividerView.setVisibility(View.GONE); } } @@ -504,9 +503,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean hasImportanceChanged() { return mSingleNotificationChannel != null && mChosenImportance != null - && (mStartingChannelImportance != mChosenImportance - || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT) - || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)); + && (mStartingChannelImportance == IMPORTANCE_UNSPECIFIED + || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT) + || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)); } private void saveImportance() { @@ -526,29 +525,76 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (mChosenImportance != null) { mMetricsLogger.write(importanceChangeLogMaker()); + int newImportance = mChosenImportance; + if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) { + if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT) + || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) { + newImportance = mStartingChannelImportance; + } + } + Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); bgHandler.post( new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, - mStartingChannelImportance, mChosenImportance)); + mStartingChannelImportance, newImportance)); } } private void updateButtons(int blockState) { - View silent = findViewById(R.id.silent_row); - View alert = findViewById(R.id.alert_row); + TextView silence = findViewById(R.id.silence); + TextView alert = findViewById(R.id.alert); + TextView done = findViewById(R.id.done); switch (blockState) { case ACTION_TOGGLE_SILENT: - silent.setBackground(mSelectedBackground); - alert.setBackground(null); + updateButtons(silence, alert); + if (mWasShownHighPriority) { + done.setText(R.string.inline_ok_button); + } else { + done.setText(R.string.inline_done_button); + } break; case ACTION_ALERT: - alert.setBackground(mSelectedBackground); - silent.setBackground(null); + updateButtons(alert, silence); + if (mWasShownHighPriority) { + done.setText(R.string.inline_done_button); + } else { + done.setText(R.string.inline_ok_button); + } break; } } + private void updateButtons(TextView selected, TextView unselected) { + selected.setBackground(mSelectedBackground); + selected.setSelected(true); + selected.setTextAppearance( + R.style.TextAppearance_NotificationImportanceButton_Selected); + unselected.setBackground(mUnselectedBackground); + unselected.setSelected(false); + unselected.setTextAppearance( + R.style.TextAppearance_NotificationImportanceButton_Unselected); + } + + void setImportanceSummary(int blockState) { + TextView view = findViewById(R.id.description); + if (blockState == ACTION_ALERT) { + view.setText(R.string.notification_channel_summary_default); + } else { + if (mShowInStatusBar) { + if (mShowOnLockscreen) { + view.setText(R.string.notification_channel_summary_low_status_lock); + } else { + view.setText(R.string.notification_channel_summary_low_status); + } + } else if (mShowOnLockscreen) { + view.setText(R.string.notification_channel_summary_low_lock); + } else { + view.setText(R.string.notification_channel_summary_low); + } + } + } + private void saveImportanceAndExitReason(@NotificationInfoAction int action) { switch (action) { case ACTION_UNDO: @@ -556,15 +602,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G break; case ACTION_DELIVER_SILENTLY: mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; - mChosenImportance = IMPORTANCE_LOW; - break; - case ACTION_TOGGLE_SILENT: - mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT; - if (mWasShownHighPriority) { - mChosenImportance = IMPORTANCE_LOW; - } else { - mChosenImportance = IMPORTANCE_DEFAULT; - } + mChosenImportance = mWasShownHighPriority + ? IMPORTANCE_LOW : mStartingChannelImportance; break; default: throw new IllegalArgumentException(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 058cb2dbdbcf..ebda5852faea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1193,6 +1193,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } + /** + * Returns best effort count of visible notifications. + */ + public int getVisibleNotificationCount() { + int count = 0; + for (int i = 0; i < getChildCount(); i++) { + final View child = getChildAt(i); + if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) { + count++; + } + } + return count; + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private boolean isCurrentlyAnimating() { return mStateAnimator.isRunning(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 0171d7f42aed..26e0a705aa5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -59,6 +59,16 @@ public class KeyguardClockPositionAlgorithm { private int mClockPreferredY; /** + * Whether or not there is a custom clock face on keyguard. + */ + private boolean mHasCustomClock; + + /** + * Whether or not the NSSL contains any visible notifications. + */ + private boolean mHasVisibleNotifs; + + /** * Height of notification stack: Sum of height of each notification. */ private int mNotificationStackHeight; @@ -117,7 +127,7 @@ public class KeyguardClockPositionAlgorithm { public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, - float dark, float emptyDragAmount) { + boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount) { mMinTopMargin = minTopMargin + mContainerTopPadding; mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; @@ -125,6 +135,8 @@ public class KeyguardClockPositionAlgorithm { mHeight = parentHeight; mKeyguardStatusHeight = keyguardStatusHeight; mClockPreferredY = clockPreferredY; + mHasCustomClock = hasCustomClock; + mHasVisibleNotifs = hasVisibleNotifs; mDarkAmount = dark; mEmptyDragAmount = emptyDragAmount; } @@ -179,6 +191,9 @@ public class KeyguardClockPositionAlgorithm { clockYDark = MathUtils.max(0, clockYDark); float clockYRegular = getExpandedClockPosition(); + if (mHasCustomClock && !mHasVisibleNotifs) { + clockYRegular = clockYDark; + } float clockYBouncer = -mKeyguardStatusHeight; // Move clock up while collapsing the shade diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 6121ae603d69..586e82c612c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -248,10 +248,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange boolean canLock = mUnlockMethodCache.isMethodSecure() && mUnlockMethodCache.canSkipBouncer(); boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled(); - boolean clickToForceLock = canLock && !clickToUnlock; - boolean longClickToForceLock = canLock && !clickToForceLock; - setClickable(clickToForceLock || clickToUnlock); - setLongClickable(longClickToForceLock); + setClickable(clickToUnlock); + setLongClickable(canLock && !clickToUnlock); setFocusable(mAccessibilityController.isAccessibilityEnabled()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 6c1a4faa03a4..742fdda429b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -178,7 +178,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback // Send the assistant availability upon connection if (isConnected) { - mNavigationBarView.setAssistantAvailable(mAssistantAvailable); + sendAssistantAvailability(mAssistantAvailable); } } @@ -235,7 +235,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback boolean available = mAssistManager .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; if (mAssistantAvailable != available) { - mNavigationBarView.setAssistantAvailable(available); + sendAssistantAvailability(available); mAssistantAvailable = available; } } @@ -715,7 +715,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } mNavigationBarView.onNavigationButtonLongPress(v); mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS); - mAssistManager.startAssist(new Bundle() /* args */); + Bundle args = new Bundle(); + args.putInt( + AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS); + mAssistManager.startAssist(args); mStatusBar.awakenDreams(); if (mNavigationBarView != null) { @@ -900,6 +903,17 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); } + private void sendAssistantAvailability(boolean available) { + if (mOverviewProxyService.getProxy() != null) { + try { + mOverviewProxyService.getProxy().onAssistantAvailable(available + && QuickStepContract.isGesturalMode(getContext())); + } catch (RemoteException e) { + Log.w(TAG, "Unable to send assistant availability data to launcher"); + } + } + } + // ----- Methods that DisplayNavigationBarController talks to ----- /** Applies auto dimming animation on navigation bar when touched. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index a45d86ef0dfe..411378f1bf07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -145,7 +145,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private NotificationPanelView mPanelView; private NavBarTintController mTintController; - private boolean mAssistantAvailable; /** * Helper that is responsible for showing the right toast when a disallowed activity operation @@ -759,23 +758,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mEdgeBackGestureHandler.onOverlaysChanged(); } - public void setAssistantAvailable(boolean available) { - mAssistantAvailable = available; - updateAssistantAvailability(); - } - - // TODO(b/112934365): move this back to NavigationBarFragment when prototype is removed - private void updateAssistantAvailability() { - boolean available = mAssistantAvailable && QuickStepContract.isGesturalMode(getContext()); - if (mOverviewProxyService.getProxy() != null) { - try { - mOverviewProxyService.getProxy().onAssistantAvailable(available); - } catch (RemoteException e) { - Log.w(TAG, "Unable to send assistant availability data to launcher"); - } - } - } - public void setMenuVisibility(final boolean show) { mContextualButtonGroup.setButtonVisiblity(R.id.menu, show); } @@ -898,10 +880,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void showPinningEnterExitToast(boolean entering) { if (entering) { mScreenPinningNotify.showPinningStartToast(); - - // TODO(b/112934365): remove after prototype finished, only needed to escape from pin - getBackButton().setVisibility(VISIBLE); - getHomeButton().setVisibility(VISIBLE); } else { mScreenPinningNotify.showPinningExitToast(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 2207e0412949..c39a4941fdab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -637,6 +637,8 @@ public class NotificationPanelView extends PanelView implements totalHeight, mKeyguardStatusView.getHeight(), clockPreferredY, + hasCustomClock(), + mNotificationStackScroller.getVisibleNotificationCount() != 0, mInterpolatedDarkAmount, mEmptyDragAmount); mClockPositionAlgorithm.run(mClockPositionResult); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 183fdb46a795..d5706e36bd21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -238,7 +238,8 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotLocation, false); // sensors off - mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, null); + mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, + mContext.getString(R.string.accessibility_sensors_off_active)); mIconController.setIconVisibility(mSlotSensorsOff, mSensorPrivacyController.isSensorPrivacyEnabled()); 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 3f33ba633d77..7c271cef6abb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -596,8 +596,8 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { - mForegroundServiceController.onAppOpChanged(code, uid, packageName, active); Dependency.get(MAIN_HANDLER).post(() -> { + mForegroundServiceController.onAppOpChanged(code, uid, packageName, active); mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active); }); } @@ -3326,7 +3326,11 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void showBouncer(boolean scrimmed) { - mStatusBarKeyguardViewManager.showBouncer(scrimmed); + if (!mIsOccluded && !scrimmed && mState == StatusBarState.KEYGUARD) { + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); + } else { + mStatusBarKeyguardViewManager.showBouncer(scrimmed); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 1e486c0949a4..d521e5534ad4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -29,6 +29,8 @@ import com.android.keyguard.KeyguardSliceView; import com.android.systemui.SystemUIFactory; import com.android.systemui.qs.QSCarrierGroup; import com.android.systemui.qs.QSFooterImpl; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.LockIcon; @@ -154,6 +156,16 @@ public class InjectionInflationController { * Creates the keyguard LockIcon. */ LockIcon createLockIcon(); + + /** + * Creates the QSPanel. + */ + QSPanel createQSPanel(); + + /** + * Creates the QuickQSPanel. + */ + QuickQSPanel createQuickQSPanel(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 8d9c5a3740b2..43912f9eb4f7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -27,6 +27,7 @@ import static android.media.AudioManager.STREAM_RING; import static android.media.AudioManager.STREAM_VOICE_CALL; import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE; import static android.view.View.GONE; +import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -228,9 +229,10 @@ public class VolumeDialogImpl implements VolumeDialog, mDialog.setContentView(R.layout.volume_dialog); mDialogView = mDialog.findViewById(R.id.volume_dialog); + mDialogView.setAlpha(0); mDialog.setCanceledOnTouchOutside(true); mDialog.setOnShowListener(dialog -> { - if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2); + if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f); mDialogView.setAlpha(0); mDialogView.animate() .alpha(1) @@ -528,7 +530,7 @@ public class VolumeDialogImpl implements VolumeDialog, if (!mHasSeenODICaptionsTooltip && !fromDismiss && mODICaptionsTooltipViewStub != null) { mController.getCaptionsComponentState(true); } else { - if (mHasSeenODICaptionsTooltip && mODICaptionsTooltipView != null) { + if (mHasSeenODICaptionsTooltip && fromDismiss && mODICaptionsTooltipView != null) { hideCaptionsTooltip(); } } @@ -565,13 +567,14 @@ public class VolumeDialogImpl implements VolumeDialog, } private void hideCaptionsTooltip() { - if (mODICaptionsTooltipView != null) { + if (mODICaptionsTooltipView != null && mODICaptionsTooltipView.getVisibility() == VISIBLE) { mODICaptionsTooltipView.animate().cancel(); mODICaptionsTooltipView.setAlpha(1.f); mODICaptionsTooltipView.animate() .alpha(0.f) .setStartDelay(0) .setDuration(DIALOG_HIDE_ANIMATION_DURATION) + .withEndAction(() -> mODICaptionsTooltipView.setVisibility(INVISIBLE)) .start(); } } @@ -677,7 +680,7 @@ public class VolumeDialogImpl implements VolumeDialog, } private void showH(int reason) { - if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]); + if (D.BUG) Log.d(TAG, "showH r=" + Events.SHOW_REASONS[reason]); mHandler.removeMessages(H.SHOW); mHandler.removeMessages(H.DISMISS); rescheduleTimeoutH(); @@ -750,7 +753,7 @@ public class VolumeDialogImpl implements VolumeDialog, mDialog.dismiss(); tryToRemoveCaptionsTooltip(); }, 50)); - if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2); + if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f); animator.start(); checkODICaptionsTooltip(true); mController.notifyVisible(false); diff --git a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml new file mode 100644 index 000000000000..5cc7976d3e60 --- /dev/null +++ b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml @@ -0,0 +1,69 @@ +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- overlayable_icons references all of the drawables in this package + that are being overlayed by resource overlays. If you remove/rename + any of these resources, you must also change the resource overlay icons.--> + <array name="overlayable_icons"> + <item>@drawable/ic_alarm</item> + <item>@drawable/ic_alarm_dim</item> + <item>@drawable/ic_bluetooth_connected</item> + <item>@drawable/ic_brightness_thumb</item> + <item>@drawable/ic_camera</item> + <item>@drawable/ic_cast</item> + <item>@drawable/ic_cast_connected</item> + <item>@drawable/ic_close_white</item> + <item>@drawable/ic_data_saver</item> + <item>@drawable/ic_data_saver_off</item> + <item>@drawable/ic_drag_handle</item> + <item>@drawable/ic_headset</item> + <item>@drawable/ic_headset_mic</item> + <item>@drawable/ic_hotspot</item> + <item>@drawable/ic_info</item> + <item>@drawable/ic_info_outline</item> + <item>@drawable/ic_invert_colors</item> + <item>@drawable/ic_location</item> + <item>@drawable/ic_lockscreen_ime</item> + <item>@drawable/ic_notifications_alert</item> + <item>@drawable/ic_notifications_silence</item> + <item>@drawable/ic_power_low</item> + <item>@drawable/ic_power_saver</item> + <item>@drawable/ic_qs_bluetooth_connecting</item> + <item>@drawable/ic_qs_bluetooth_connecting</item> + <item>@drawable/ic_qs_cancel</item> + <item>@drawable/ic_qs_no_sim</item> + <item>@drawable/ic_qs_wifi_0</item> + <item>@drawable/ic_qs_wifi_1</item> + <item>@drawable/ic_qs_wifi_2</item> + <item>@drawable/ic_qs_wifi_3</item> + <item>@drawable/ic_qs_wifi_4</item> + <item>@drawable/ic_qs_wifi_disconnected</item> + <item>@drawable/ic_screenshot_delete</item> + <item>@drawable/ic_settings</item> + <item>@drawable/ic_swap_vert</item> + <item>@drawable/ic_tune_black_16dp</item> + <item>@drawable/ic_volume_alarm_mute</item> + <item>@drawable/ic_volume_bt_sco</item> + <item>@drawable/ic_volume_media</item> + <item>@drawable/ic_volume_media_mute</item> + <item>@drawable/ic_volume_ringer</item> + <item>@drawable/ic_volume_ringer_mute</item> + <item>@drawable/ic_volume_ringer_vibrate</item> + <item>@drawable/ic_volume_voice</item> + <item>@drawable/stat_sys_mic_none</item> + <item>@drawable/stat_sys_vpn_ic</item> + </array> +</resources> diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java index c6e85c3f81a0..19e1a5cef183 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java @@ -95,6 +95,22 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase { } @Test + public void testUnregisterWithoutRegister() { + Uri testUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("something") + .path("test") + .build(); + SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler(); + relayHandler.mContext = spy(mContext); + + Intent intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER); + intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0)); + relayHandler.handleIntent(intent); + // No crash + } + + @Test public void testRelay() { Receiver.sReceiver = mock(BroadcastReceiver.class); Uri testUri = new Uri.Builder() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java index da31134d13b4..bafae6ce737a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.View; import android.widget.TextView; import androidx.test.filters.SmallTest; @@ -53,22 +52,11 @@ public class BubbleStackViewTest extends SysuiTestCase { } @Test - public void testAnimateInFlyoutForBubble() throws InterruptedException { + public void testAnimateInFlyoutForBubble() { when(mNotifEntry.getUpdateMessage(any())).thenReturn("Test Flyout Message."); mStackView.animateInFlyoutForBubble(mBubble); - // Wait for the fade in. - Thread.sleep(200); - - // Flyout should be visible and showing our text. - assertEquals(1f, mStackView.findViewById(R.id.bubble_flyout).getAlpha(), .01f); assertEquals("Test Flyout Message.", ((TextView) mStackView.findViewById(R.id.bubble_flyout_text)).getText()); - - // Wait until it should have gone away. - Thread.sleep(BubbleStackView.FLYOUT_HIDE_AFTER + 200); - - // Flyout should be gone. - assertEquals(View.GONE, mStackView.findViewById(R.id.bubble_flyout).getVisibility()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 10d1572ca621..db4f5ffcdfeb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.keyguard.CarrierText; import com.android.systemui.Dependency; +import com.android.systemui.DumpController; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiBaseFragmentTest; @@ -95,7 +96,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { QSTileHost host = new QSTileHost(mContext, mock(StatusBarIconController.class), mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(), mock(PluginManager.class), mock(TunerService.class), - () -> mock(AutoTileManager.class)); + () -> mock(AutoTileManager.class), mock(DumpController.class)); qs.setHost(host); qs.setListening(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index c6b31d03ff3b..38cdee4c090b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java @@ -14,10 +14,12 @@ package com.android.systemui.qs; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -34,9 +36,9 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.customize.QSCustomizer; +import com.android.systemui.qs.tileimpl.QSTileImpl; import org.junit.Before; import org.junit.Test; @@ -44,6 +46,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Collections; @RunWith(AndroidTestingRunner.class) @@ -59,10 +64,12 @@ public class QSPanelTest extends SysuiTestCase { @Mock private QSCustomizer mCustomizer; @Mock - private QSTile dndTile; + private QSTileImpl dndTile; private ViewGroup mParentView; @Mock private QSDetail.Callback mCallback; + @Mock + private QSTileView mQSTileView; @Before public void setup() throws Exception { @@ -78,7 +85,7 @@ public class QSPanelTest extends SysuiTestCase { when(dndTile.getTileSpec()).thenReturn("dnd"); when(mHost.getTiles()).thenReturn(Collections.emptyList()); - when(mHost.createTileView(any(), anyBoolean())).thenReturn(mock(QSTileView.class)); + when(mHost.createTileView(any(), anyBoolean())).thenReturn(mQSTileView); mQsPanel.setHost(mHost, mCustomizer); mQsPanel.addTile(dndTile, true); @@ -120,4 +127,27 @@ public class QSPanelTest extends SysuiTestCase { verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt()); } + + @Test + public void testDump() { + String mockTileViewString = "Mock Tile View"; + String mockTileString = "Mock Tile"; + doAnswer(invocation -> { + PrintWriter pw = invocation.getArgument(1); + pw.println(mockTileString); + return null; + }).when(dndTile).dump(any(FileDescriptor.class), any(PrintWriter.class), + any(String[].class)); + when(mQSTileView.toString()).thenReturn(mockTileViewString); + + StringWriter w = new StringWriter(); + PrintWriter pw = new PrintWriter(w); + mQsPanel.dump(mock(FileDescriptor.class), pw, new String[]{}); + String expected = "QSPanel:\n" + + " Tile records:\n" + + " " + mockTileString + "\n" + + " " + mockTileViewString + "\n"; + assertEquals(expected, w.getBuffer().toString()); + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index a458928da9d8..f73472f86d8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -17,23 +17,100 @@ package com.android.systemui.qs; +import static junit.framework.Assert.assertEquals; import static junit.framework.TestCase.assertFalse; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.DumpController; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.qs.QSFactory; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.qs.tileimpl.QSFactoryImpl; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.phone.AutoTileManager; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.tuner.TunerService; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; +import javax.inject.Provider; + @RunWith(AndroidTestingRunner.class) @SmallTest +@RunWithLooper public class QSTileHostTest extends SysuiTestCase { + private static String MOCK_STATE_STRING = "MockState"; + + @Mock + private StatusBarIconController mIconController; + @Mock + private QSFactoryImpl mDefaultFactory; + @Mock + private PluginManager mPluginManager; + @Mock + private TunerService mTunerService; + @Mock + private Provider<AutoTileManager> mAutoTiles; + @Mock + private DumpController mDumpController; + @Mock + private QSTile.State mMockState; + private Handler mHandler; + private TestableLooper mLooper; + private QSTileHost mQSTileHost; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mLooper = TestableLooper.get(this); + mHandler = new Handler(mLooper.getLooper()); + mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, + mLooper.getLooper(), + mPluginManager, mTunerService, mAutoTiles, mDumpController); + setUpTileFactory(); + } + + private void setUpTileFactory() { + when(mMockState.toString()).thenReturn(MOCK_STATE_STRING); + when(mDefaultFactory.createTile(anyString())).thenAnswer( + invocation -> { + String spec = invocation.getArgument(0); + switch (spec) { + case "spec1": + return new TestTile1(mQSTileHost); + case "spec2": + return new TestTile2(mQSTileHost); + default: + return null; + } + }); + + } + @Test public void testLoadTileSpecs_emptySetting() { List<String> tiles = QSTileHost.loadTileSpecs(mContext, ""); @@ -45,4 +122,91 @@ public class QSTileHostTest extends SysuiTestCase { List<String> tiles = QSTileHost.loadTileSpecs(mContext, null); assertFalse(tiles.isEmpty()); } + + @Test + public void testDump() { + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2"); + StringWriter w = new StringWriter(); + PrintWriter pw = new PrintWriter(w); + mQSTileHost.dump(mock(FileDescriptor.class), pw, new String[]{}); + String output = "QSTileHost:\n" + + TestTile1.class.getSimpleName() + ":\n" + + " " + MOCK_STATE_STRING + "\n" + + TestTile2.class.getSimpleName() + ":\n" + + " " + MOCK_STATE_STRING + "\n"; + assertEquals(output, w.getBuffer().toString()); + } + + private static class TestQSTileHost extends QSTileHost { + TestQSTileHost(Context context, StatusBarIconController iconController, + QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper, + PluginManager pluginManager, TunerService tunerService, + Provider<AutoTileManager> autoTiles, DumpController dumpController) { + super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager, + tunerService, autoTiles, dumpController); + } + + @Override + public void onPluginConnected(QSFactory plugin, Context pluginContext) { + } + + @Override + public void onPluginDisconnected(QSFactory plugin) { + } + } + + private class TestTile extends QSTileImpl<QSTile.State> { + + protected TestTile(QSHost host) { + super(host); + } + + @Override + public State newTileState() { + return mMockState; + } + + @Override + public State getState() { + return mMockState; + } + + @Override + protected void handleClick() {} + + @Override + protected void handleUpdateState(State state, Object arg) {} + + @Override + public int getMetricsCategory() { + return 0; + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + protected void handleSetListening(boolean listening) {} + + @Override + public CharSequence getTileLabel() { + return null; + } + } + + private class TestTile1 extends TestTile { + + protected TestTile1(QSHost host) { + super(host); + } + } + + private class TestTile2 extends TestTile { + + protected TestTile2(QSHost host) { + super(host); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index 03278b4aadc6..183e027478df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -29,6 +29,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import com.android.systemui.DumpController; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.tileimpl.QSFactoryImpl; @@ -67,7 +68,8 @@ public class TileServicesTest extends SysuiTestCase { Looper.myLooper(), mock(PluginManager.class), mock(TunerService.class), - () -> mock(AutoTileManager.class)); + () -> mock(AutoTileManager.class), + mock(DumpController.class)); mTileService = new TestTileServices(host, Looper.getMainLooper()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java index 8a36cfbc4e2e..63ebe9290f64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java @@ -14,6 +14,8 @@ package com.android.systemui.qs.tileimpl; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; @@ -43,6 +45,7 @@ import org.junit.runner.RunWith; public class QSIconViewImplTest extends SysuiTestCase { private QSIconViewImpl mIconView; + private static int RES_ID = 1; @Before public void setup() { @@ -81,4 +84,24 @@ public class QSIconViewImplTest extends SysuiTestCase { mIconView.setIcon(iv, s, true); verify(iv).setImageTintList(argThat(stateList -> stateList.getColors()[0] == desiredColor)); } + + @Test + public void testStateSetCorrectly_toString() { + ImageView iv = mock(ImageView.class); + State s = new State(); + s.state = Tile.STATE_ACTIVE; + int desiredColor = mIconView.getColor(s.state); + Icon i = mock(Icon.class); + s.icon = i; + when(i.toString()).thenReturn("MOCK ICON"); + mIconView.setIcon(iv, s, false); + + assertEquals("QSIconViewImpl[state=" + Tile.STATE_ACTIVE + ", tint=" + desiredColor + + ", lastIcon=" + i.toString() + "]", mIconView.toString()); + } + + @Test + public void testIconNotSet_toString() { + assertFalse(mIconView.toString().contains("lastIcon")); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 3df8a8aadda2..192d8f8e8226 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -34,6 +34,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static java.lang.Thread.sleep; + import android.content.Intent; import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; @@ -60,8 +62,6 @@ import org.mockito.ArgumentMatcher; import org.mockito.Captor; import org.mockito.MockitoAnnotations; -import static java.lang.Thread.sleep; - @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 80c51cfd1d34..375b6e54db3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -20,6 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -46,11 +48,14 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -72,6 +77,10 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private LockIcon mLockIcon; @Mock private LockPatternUtils mLockPatternUtils; + @Mock + private ShadeController mShadeController; + @Mock + private AccessibilityController mAccessibilityController; private KeyguardIndicationTextView mTextView; private KeyguardIndicationController mController; @@ -102,7 +111,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { Looper.prepare(); } mController = new KeyguardIndicationController(mContext, mIndicationArea, mLockIcon, - mLockPatternUtils, mWakeLock); + mLockPatternUtils, mWakeLock, mShadeController, mAccessibilityController); } @Test @@ -222,4 +231,23 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE); assertThat(mTextView.getAlpha()).isEqualTo(1f); } + + @Test + public void lockIcon_click() { + createController(); + + ArgumentCaptor<View.OnLongClickListener> longClickCaptor = ArgumentCaptor.forClass( + View.OnLongClickListener.class); + ArgumentCaptor<View.OnClickListener> clickCaptor = ArgumentCaptor.forClass( + View.OnClickListener.class); + verify(mLockIcon).setOnLongClickListener(longClickCaptor.capture()); + verify(mLockIcon).setOnClickListener(clickCaptor.capture()); + + when(mAccessibilityController.isAccessibilityEnabled()).thenReturn(true); + clickCaptor.getValue().onClick(mLockIcon); + verify(mShadeController).showBouncer(eq(false)); + + longClickCaptor.getValue().onLongClick(mLockIcon); + verify(mLockPatternUtils).requireCredentialEntry(anyInt()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index d2f8e02311e6..02731c0e8e18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -250,8 +250,6 @@ public class NotificationInfoTest extends SysuiTestCase { IMPORTANCE_DEFAULT, true); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); - final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); - assertEquals(GONE, groupDividerView.getVisibility()); } @Test @@ -268,8 +266,6 @@ public class NotificationInfoTest extends SysuiTestCase { final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); - final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); - assertEquals(View.VISIBLE, groupDividerView.getVisibility()); } @Test @@ -486,7 +482,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, IMPORTANCE_LOW, false); - mNotificationInfo.findViewById(R.id.alert_row).performClick(); + mNotificationInfo.findViewById(R.id.alert).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -500,7 +496,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, IMPORTANCE_DEFAULT, true); - mNotificationInfo.findViewById(R.id.silent_row).performClick(); + mNotificationInfo.findViewById(R.id.silence).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -527,7 +523,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - IMPORTANCE_DEFAULT, true); + IMPORTANCE_UNSPECIFIED, true); mNotificationInfo.handleCloseControls(true, false); @@ -542,6 +538,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { NotificationInfo.CheckSaveListener listener = mock(NotificationInfo.CheckSaveListener.class); + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, @@ -570,6 +567,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { NotificationInfo.CheckSaveListener listener = mock(NotificationInfo.CheckSaveListener.class); + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, @@ -588,6 +586,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { NotificationInfo.CheckSaveListener listener = mock(NotificationInfo.CheckSaveListener.class); + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, @@ -603,6 +602,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControls_blockingHelperDismissedIfShown() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, @@ -629,7 +629,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testSilentlyChangedCallsUpdateNotificationChannel_blockingHelper() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification( mMockPackageManager, mMockINotificationManager, @@ -644,7 +644,7 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, IMPORTANCE_DEFAULT, - false); + true); mNotificationInfo.findViewById(R.id.deliver_silently).performClick(); waitForUndoButton(); @@ -684,7 +684,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - IMPORTANCE_DEFAULT, false); + IMPORTANCE_DEFAULT, true); mNotificationInfo.handleCloseControls(true, false); @@ -704,7 +704,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, IMPORTANCE_DEFAULT, true); - mNotificationInfo.findViewById(R.id.silent_row).performClick(); + mNotificationInfo.findViewById(R.id.silence).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(true, false); @@ -723,9 +723,9 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - IMPORTANCE_DEFAULT, false); + IMPORTANCE_LOW, false); - mNotificationInfo.findViewById(R.id.alert_row).performClick(); + mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(true, false); @@ -745,9 +745,9 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - IMPORTANCE_DEFAULT, true); + IMPORTANCE_UNSPECIFIED, true); - mNotificationInfo.findViewById(R.id.silent_row).performClick(); + mNotificationInfo.findViewById(R.id.silence).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(true, false); @@ -762,14 +762,82 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testSilenceCallsUpdateNotificationChannel_channelImportanceMin() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_MIN); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_MIN, false); + + assertEquals(mContext.getString(R.string.inline_done_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + mNotificationInfo.findViewById(R.id.silence).performClick(); + assertEquals(mContext.getString(R.string.inline_done_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + mNotificationInfo.findViewById(R.id.done).performClick(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields()& USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_MIN, updated.getValue().getImportance()); + } + + @Test + public void testAlertCallsUpdateNotificationChannel_channelImportanceMin() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_MIN); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_MIN, false); + + assertEquals(mContext.getString(R.string.inline_done_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + mNotificationInfo.findViewById(R.id.alert).performClick(); + assertEquals(mContext.getString(R.string.inline_ok_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + mNotificationInfo.findViewById(R.id.done).performClick(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields()& USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance()); + } + + @Test + public void testDoneText() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + IMPORTANCE_LOW, false); + + assertEquals(mContext.getString(R.string.inline_done_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + mNotificationInfo.findViewById(R.id.alert).performClick(); + assertEquals(mContext.getString(R.string.inline_ok_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + mNotificationInfo.findViewById(R.id.silence).performClick(); + assertEquals(mContext.getString(R.string.inline_done_button), + ((TextView) mNotificationInfo.findViewById(R.id.done)).getText()); + } + + @Test public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, IMPORTANCE_LOW, false); - mNotificationInfo.findViewById(R.id.alert_row).performClick(); + mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(true, false); @@ -790,7 +858,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, IMPORTANCE_LOW, false); - mNotificationInfo.findViewById(R.id.alert_row).performClick(); + mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); mNotificationInfo.handleCloseControls(false, false); @@ -809,7 +877,7 @@ public class NotificationInfoTest extends SysuiTestCase { }, null, null, true, false, IMPORTANCE_LOW, false ); - mNotificationInfo.findViewById(R.id.alert_row).performClick(); + mNotificationInfo.findViewById(R.id.alert).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 93546488ad0a..f191cabbb3d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -41,6 +41,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private static final float ZERO_DRAG = 0.f; private static final float OPAQUE = 1.f; private static final float TRANSPARENT = 0.f; + private static final boolean HAS_CUSTOM_CLOCK = false; + private static final boolean HAS_VISIBLE_NOTIFS = false; private KeyguardClockPositionAlgorithm mClockPositionAlgorithm; private KeyguardClockPositionAlgorithm.Result mClockPosition; @@ -48,11 +50,18 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private float mPanelExpansion; private int mKeyguardStatusHeight; private float mDark; + private int mPreferredClockY; + private boolean mHasCustomClock; + private boolean mHasVisibleNotifs; @Before public void setUp() { mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); mClockPosition = new KeyguardClockPositionAlgorithm.Result(); + + mPreferredClockY = PREFERRED_CLOCK_Y; + mHasCustomClock = HAS_CUSTOM_CLOCK; + mHasVisibleNotifs = HAS_VISIBLE_NOTIFS; } @Test @@ -293,6 +302,71 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0); } + @Test + public void preferredCustomClockPositionNoNotifications() { + // GIVEN on the lock screen with a custom clock and no visible notifications + givenLockScreen(); + mPreferredClockY = 100; + mHasCustomClock = true; + mHasVisibleNotifs = false; + // AND given empty height for clock and stack scroller + mNotificationStackHeight = EMPTY_HEIGHT; + mKeyguardStatusHeight = EMPTY_HEIGHT; + // WHEN the clock position algorithm is run + positionClock(); + // THEN the clock Y position is the preferred Y position. + assertThat(mClockPosition.clockY).isEqualTo(100); + } + + @Test + public void preferredDefaultClockPositionNoNotifications() { + // GIVEN on the lock screen with a custom clock and no visible notifications + givenLockScreen(); + mPreferredClockY = 100; + mHasCustomClock = false; + mHasVisibleNotifs = false; + // AND given empty height for clock and stack scroller + mNotificationStackHeight = EMPTY_HEIGHT; + mKeyguardStatusHeight = EMPTY_HEIGHT; + // WHEN the clock position algorithm is run + positionClock(); + // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2) and not + // preferred. + assertThat(mClockPosition.clockY).isEqualTo(1000); + } + + @Test + public void preferredCustomClockPositionWithVisibleNotifications() { + // GIVEN on the lock screen with a custom clock and visible notifications + givenLockScreen(); + mPreferredClockY = 100; + mHasCustomClock = true; + mHasVisibleNotifs = true; + // AND given empty height for clock and stack scroller + mNotificationStackHeight = EMPTY_HEIGHT; + mKeyguardStatusHeight = EMPTY_HEIGHT; + // WHEN the clock position algorithm is run + positionClock(); + // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2). + assertThat(mClockPosition.clockY).isEqualTo(1000); + } + + @Test + public void preferredCustomClockPositionWithVisibleNotificationsOnAod() { + // GIVEN on the lock screen with a custom clock and visible notifications + givenAOD(); + mPreferredClockY = 100; + mHasCustomClock = true; + mHasVisibleNotifs = true; + // AND given empty height for clock and stack scroller + mNotificationStackHeight = EMPTY_HEIGHT; + mKeyguardStatusHeight = EMPTY_HEIGHT; + // WHEN the clock position algorithm is run + positionClock(); + // THEN the clock Y position is the preferred Y position. + assertThat(mClockPosition.clockY).isEqualTo(100); + } + private void givenAOD() { mPanelExpansion = 1.f; mDark = 1.f; @@ -305,8 +379,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void positionClock() { mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, - mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, PREFERRED_CLOCK_Y, mDark, - ZERO_DRAG); + mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY, + mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG); mClockPositionAlgorithm.run(mClockPosition); } } diff --git a/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml index c420ab6da984..a99f705dbde8 100644 --- a/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml +++ b/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml @@ -18,5 +18,5 @@ --> <resources> <color name="accent_device_default_light">#AF6050</color> - <color name="accent_device_default_dark">#9D6962</color> + <color name="accent_device_default_dark">#C3A6A2</color> </resources> diff --git a/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml index 6aec8059053b..449639bdfef2 100644 --- a/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml +++ b/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml @@ -18,5 +18,5 @@ --> <resources> <color name="accent_device_default_light">#0C80A7</color> - <color name="accent_device_default_dark">#347D98</color> + <color name="accent_device_default_dark">#28BDD7</color> </resources> diff --git a/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml index 049f8b81c1f1..47079a8d9c3a 100644 --- a/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml +++ b/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml @@ -18,5 +18,5 @@ --> <resources> <color name="accent_device_default_light">#C42CC9</color> - <color name="accent_device_default_dark">#C42CC9</color> + <color name="accent_device_default_dark">#E68AED</color> </resources> diff --git a/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml index b6f757cfafe9..f147aeb79bc9 100644 --- a/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml +++ b/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml @@ -18,5 +18,5 @@ --> <resources> <color name="accent_device_default_light">#47618A</color> - <color name="accent_device_default_dark">#5D7A92</color> + <color name="accent_device_default_dark">#99ACCC</color> </resources> diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/config.xml b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml index 7b655557acea..2016ece4d2c7 100644 --- a/packages/overlays/IconShapeSquareOverlay/res/values/config.xml +++ b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml @@ -23,6 +23,8 @@ <bool name="config_useRoundIcon">false</bool> <!-- Corner radius of system dialogs --> <dimen name="config_dialogCornerRadius">0dp</dimen> - + <!-- Corner radius for bottom sheet system dialogs --> + <dimen name="config_bottomDialogCornerRadius">0dp</dimen> + </resources> diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 3babb6d943f5..21c6035da038 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -7182,6 +7182,30 @@ message MetricsEvent { // OS: Q QS_UI_MODE_NIGHT = 1706; + // OPEN: Settings > Pick SIM dialog + // CATEGORY: SETTINGS + // OS: Q + DIALOG_SIM_LIST = 1707; + + // OPEN: Settings > Pick SIM (that supports calling) dialog + // CATEGORY: SETTINGS + // OS: Q + DIALOG_CALL_SIM_LIST = 1708; + + // OPEN: Settings > Pick preferred SIM dialog + // CATEGORY: SETTINGS + // OS: Q + DIALOG_PREFERRED_SIM_PICKER = 1709; + + // ACTION: Share a Wi-Fi network by generating a QR code + ACTION_SETTINGS_SHARE_WIFI_QR_CODE = 1710; + + // ACTION: Connect to a Wi-Fi network by scanning a QR code + ACTION_SETTINGS_ENROLL_WIFI_QR_CODE = 1711; + + // ACTION: Share Wi-Fi hotspot by generating a QR code + ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 834987cb6438..ea044cfa75d5 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -2098,6 +2098,9 @@ message WifiUsabilityStats { // Firmware alert code. Only valid when the stats was triggered by a firmware // alert, otherwise -1. optional int32 firmware_alert_code = 4 [default = -1]; + + // Absolute milliseconds from device boot when these stats were sampled + optional int64 time_stamp_ms = 5; } message DeviceMobilityStatePnoScanStats { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 6e2c2280a8b9..05b937a34626 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2504,7 +2504,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * registered. */ @Override - public int getAccessibilityWindowId(IBinder windowToken) { + public int getAccessibilityWindowId(@Nullable IBinder windowToken) { synchronized (mLock) { if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { throw new SecurityException("Only SYSTEM can call getAccessibilityWindowId"); @@ -2722,9 +2722,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return -1; } - private void notifyOutsideTouchIfNeeded(int targetWindowId, int action, Bundle arguments, - int interactionId, IAccessibilityInteractionConnectionCallback callback, int fetchFlags, - int interrogatingPid, long interrogatingTid) { + private void notifyOutsideTouchIfNeeded(int targetWindowId, int action) { if (action != ACTION_CLICK && action != ACTION_LONG_CLICK) { return; } @@ -2741,13 +2739,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final RemoteAccessibilityConnection connection = connectionList.get(i); if (connection != null) { try { - connection.mConnection.performAccessibilityAction( - AccessibilityNodeInfo.ROOT_ITEM_ID, - R.id.accessibilityActionOutsideTouch, arguments, interactionId, - callback, fetchFlags, interrogatingPid, interrogatingTid); + connection.getRemote().notifyOutsideTouch(); } catch (RemoteException re) { if (DEBUG) { - Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re); + Slog.e(LOG_TAG, "Error calling notifyOutsideTouch()"); } } } @@ -2833,8 +2828,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mPowerManager.userActivity(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); - notifyOutsideTouchIfNeeded(resolvedWindowId, action, arguments, interactionId, callback, - fetchFlags, interrogatingPid, interrogatingTid); + notifyOutsideTouchIfNeeded(resolvedWindowId, action); if (activityToken != null) { LocalServices.getService(ActivityTaskManagerInternal.class) .setFocusedActivity(activityToken); @@ -3790,7 +3784,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private List<Integer> getWatchOutsideTouchWindowIdLocked(int targetWindowId) { final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); - if (targetWindow != null && mWindowInfoById != null && mHasWatchOutsideTouchWindow) { + if (targetWindow != null && mHasWatchOutsideTouchWindow) { final List<Integer> outsideWindowsId = new ArrayList<>(); for (int i = 0; i < mWindowInfoById.size(); i++) { WindowInfo window = mWindowInfoById.valueAt(i); diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 7f411d83b34b..a2d3d4c25b1d 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -781,36 +781,46 @@ public final class ContentCaptureManagerService extends @GuardedBy("mGlobalWhitelistStateLock") public ContentCaptureOptions getOptions(@UserIdInt int userId, @NonNull String packageName) { + boolean packageWhitelisted; + ArraySet<ComponentName> whitelistedComponents = null; synchronized (mGlobalWhitelistStateLock) { - if (!isWhitelisted(userId, packageName)) { - if (packageName.equals(mServicePackages.get(userId))) { + packageWhitelisted = isWhitelisted(userId, packageName); + if (!packageWhitelisted) { + // Full package is not whitelisted: check individual components first + whitelistedComponents = getWhitelistedComponents(userId, packageName); + if (whitelistedComponents == null + && packageName.equals(mServicePackages.get(userId))) { + // No components whitelisted either, but let it go because it's the + // service's own package if (verbose) Slog.v(mTag, "getOptionsForPackage() lite for " + packageName); return new ContentCaptureOptions(mDevCfgLoggingLevel); } - if (verbose) { - Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted"); - } - return null; } + } // synchronized - final ArraySet<ComponentName> whitelistedComponents = - getWhitelistedComponents(userId, packageName); - if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) { - if (!packageName.equals(mServicePackages.get(userId))) { - Slog.w(mTag, "Ignoring package " + packageName - + " while using temporary service " + mServicePackages.get(userId)); - return null; - } + // Restrict what temporary services can whitelist + if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) { + if (!packageName.equals(mServicePackages.get(userId))) { + Slog.w(mTag, "Ignoring package " + packageName + " while using temporary " + + "service " + mServicePackages.get(userId)); + return null; } - final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, - mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, - mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, - whitelistedComponents); + } + + if (!packageWhitelisted && whitelistedComponents == null) { + // No can do! if (verbose) { - Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options); + Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted"); } - return options; + return null; } + + final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, + mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, + mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, + whitelistedComponents); + if (verbose) Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options); + return options; } @Override diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 5a42e7893ab4..a7921b5f3892 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -35,13 +35,11 @@ import android.app.ActivityManagerInternal; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.content.ComponentName; -import android.content.ContentCaptureOptions; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; @@ -60,7 +58,6 @@ import android.view.contentcapture.ContentCaptureCondition; import android.view.contentcapture.DataRemovalRequest; import com.android.internal.annotations.GuardedBy; -import com.android.internal.infra.WhitelistHelper; import com.android.internal.os.IResultReceiver; import com.android.server.LocalServices; import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks; @@ -97,12 +94,6 @@ final class ContentCapturePerUserService new ContentCaptureServiceRemoteCallback(); /** - * List of packages that are whitelisted to be content captured. - */ - @GuardedBy("mLock") - private final WhitelistHelper mWhitelistHelper = new WhitelistHelper(); - - /** * List of conditions keyed by package. */ @GuardedBy("mLock") @@ -131,6 +122,7 @@ final class ContentCapturePerUserService * Updates the reference to the remote service. */ private void updateRemoteServiceLocked(boolean disabled) { + if (mMaster.verbose) Slog.v(TAG, "updateRemoteService(disabled=" + disabled + ")"); if (mRemoteService != null) { if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service"); mRemoteService.destroy(); @@ -247,7 +239,8 @@ final class ContentCapturePerUserService final int displayId = activityPresentationInfo.displayId; final ComponentName componentName = activityPresentationInfo.componentName; final boolean whiteListed = mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId, - componentName); + componentName) || mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId, + componentName.getPackageName()); final ComponentName serviceComponentName = getServiceComponentName(); final boolean enabled = isEnabledLocked(); if (mMaster.mRequestsHistory != null) { @@ -462,40 +455,6 @@ final class ContentCapturePerUserService @GuardedBy("mLock") @Nullable - ContentCaptureOptions getOptionsForPackageLocked(@NonNull String packageName) { - if (!mWhitelistHelper.isWhitelisted(packageName)) { - if (packageName.equals(getServicePackageName())) { - if (mMaster.verbose) Slog.v(mTag, "getOptionsForPackage() lite for " + packageName); - return new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel); - } - if (mMaster.verbose) { - Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted"); - } - return null; - } - - final ArraySet<ComponentName> whitelistedComponents = mWhitelistHelper - .getWhitelistedComponents(packageName); - if (Build.IS_USER && isTemporaryServiceSetLocked()) { - final String servicePackageName = getServicePackageName(); - if (!packageName.equals(servicePackageName)) { - Slog.w(mTag, "Ignoring package " + packageName - + " while using temporary service " + servicePackageName); - return null; - } - } - final ContentCaptureOptions options = new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel, - mMaster.mDevCfgMaxBufferSize, mMaster.mDevCfgIdleFlushingFrequencyMs, - mMaster.mDevCfgTextChangeFlushingFrequencyMs, mMaster.mDevCfgLogHistorySize, - whitelistedComponents); - if (mMaster.verbose) { - Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options); - } - return options; - } - - @GuardedBy("mLock") - @Nullable ArraySet<ContentCaptureCondition> getContentCaptureConditionsLocked( @NonNull String packageName) { return mConditionsByPkg.get(packageName); diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 1bd367cdfa94..1fa62cec6e30 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3152,7 +3152,7 @@ class AlarmManagerService extends SystemService { void removeLocked(final int uid) { if (uid == Process.SYSTEM_UID) { - Slog.wtf(TAG, "removeLocked: Shouldn't for UID=" + uid); + // If a force-stop occurs for a system-uid package, ignore it. return; } boolean didRemove = false; @@ -3247,7 +3247,7 @@ class AlarmManagerService extends SystemService { // Only called for ephemeral apps void removeForStoppedLocked(final int uid) { if (uid == Process.SYSTEM_UID) { - Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for UID=" + uid); + // If a force-stop occurs for a system-uid package, ignore it. return; } boolean didRemove = false; @@ -3291,7 +3291,7 @@ class AlarmManagerService extends SystemService { void removeUserLocked(int userHandle) { if (userHandle == UserHandle.USER_SYSTEM) { - Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for user=" + userHandle); + // If we're told we're removing the system user, ignore it. return; } boolean didRemove = false; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e4c39ccd629c..ee8bb0524dfd 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -47,6 +47,7 @@ import static android.system.OsConstants.IPPROTO_UDP; import static com.android.internal.util.Preconditions.checkNotNull; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.BroadcastOptions; import android.app.NotificationManager; @@ -632,7 +633,8 @@ public class ConnectivityService extends IConnectivityManager.Stub * the first network for a given type changes, or if the default network * changes. */ - private class LegacyTypeTracker { + @VisibleForTesting + static class LegacyTypeTracker { private static final boolean DBG = true; private static final boolean VDBG = false; @@ -658,10 +660,12 @@ public class ConnectivityService extends IConnectivityManager.Stub * - dump is thread-safe with respect to concurrent add and remove calls. */ private final ArrayList<NetworkAgentInfo> mTypeLists[]; + @NonNull + private final ConnectivityService mService; - public LegacyTypeTracker() { - mTypeLists = (ArrayList<NetworkAgentInfo>[]) - new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; + LegacyTypeTracker(@NonNull ConnectivityService service) { + mService = service; + mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; } public void addSupportedType(int type) { @@ -710,10 +714,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Send a broadcast if this is the first network of its type or if it's the default. - final boolean isDefaultNetwork = isDefaultNetwork(nai); + final boolean isDefaultNetwork = mService.isDefaultNetwork(nai); if ((list.size() == 1) || isDefaultNetwork) { maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork); - sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type); + mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type); } } @@ -731,19 +735,18 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - final DetailedState state = DetailedState.DISCONNECTED; - if (wasFirstNetwork || wasDefault) { - maybeLogBroadcast(nai, state, type, wasDefault); - sendLegacyNetworkBroadcast(nai, state, type); + maybeLogBroadcast(nai, DetailedState.DISCONNECTED, type, wasDefault); + mService.sendLegacyNetworkBroadcast(nai, DetailedState.DISCONNECTED, type); } if (!list.isEmpty() && wasFirstNetwork) { if (DBG) log("Other network available for type " + type + ", sending connected broadcast"); final NetworkAgentInfo replacement = list.get(0); - maybeLogBroadcast(replacement, state, type, isDefaultNetwork(replacement)); - sendLegacyNetworkBroadcast(replacement, state, type); + maybeLogBroadcast(replacement, DetailedState.CONNECTED, type, + mService.isDefaultNetwork(replacement)); + mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type); } } @@ -758,7 +761,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // send out another legacy broadcast - currently only used for suspend/unsuspend // toggle public void update(NetworkAgentInfo nai) { - final boolean isDefault = isDefaultNetwork(nai); + final boolean isDefault = mService.isDefaultNetwork(nai); final DetailedState state = nai.networkInfo.getDetailedState(); for (int type = 0; type < mTypeLists.length; type++) { final ArrayList<NetworkAgentInfo> list = mTypeLists[type]; @@ -766,7 +769,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean isFirst = contains && (nai == list.get(0)); if (isFirst || contains && isDefault) { maybeLogBroadcast(nai, state, type, isDefault); - sendLegacyNetworkBroadcast(nai, state, type); + mService.sendLegacyNetworkBroadcast(nai, state, type); } } } @@ -802,7 +805,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); } } - private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(); + private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this); /** * Helper class which parses out priority arguments and dumps sections according to their @@ -2808,6 +2811,11 @@ public class ConnectivityService extends IConnectivityManager.Stub EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, mNai.network.netId)); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } private boolean networkRequiresValidation(NetworkAgentInfo nai) { @@ -5357,7 +5365,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private boolean isDefaultNetwork(NetworkAgentInfo nai) { + @VisibleForTesting + protected boolean isDefaultNetwork(NetworkAgentInfo nai) { return nai == getDefaultNetwork(); } @@ -6565,7 +6574,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) { + @VisibleForTesting + protected void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) { // The NetworkInfo we actually send out has no bearing on the real // state of affairs. For example, if the default connection is mobile, // and a request for HIPRI has just gone away, we need to pretend that diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index 6b42b3d06d4b..5bd27c4dd481 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -159,7 +159,13 @@ public abstract class ExtconUEventObserver extends UEventObserver { } } - /** Does the {@link /sys/class/extcon} directory exist */ + /** Does the {@code /sys/class/extcon/<name>} directory exist */ + public static boolean namedExtconDirExists(String name) { + File extconDir = new File("/sys/class/extcon/" + name); + return extconDir.exists() && extconDir.isDirectory(); + } + + /** Does the {@code /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); return extconDir.exists() && extconDir.isDirectory(); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 3410d8d86852..44fc45e13218 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -88,6 +88,7 @@ import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; @@ -3567,6 +3568,10 @@ public class LocationManagerService extends ILocationManager.Stub { return; } pw.println("Current Location Manager state:"); + pw.print(" Current System Time: " + + TimeUtils.logTimeOfDay(System.currentTimeMillis())); + pw.println(", Current Elapsed Time: " + + TimeUtils.formatDuration(SystemClock.elapsedRealtime())); pw.println(" Current user: " + mCurrentUserId + " " + Arrays.toString( mCurrentUserProfiles)); pw.println(" Location mode: " + isLocationEnabled()); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index ce1d33a06f80..6c1ffa7225f0 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3999,8 +3999,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void onExternalStoragePolicyChanged(int uid, String packageName) { - // No runtime storage permissions in isolated storage world, so nothing to do here. - if (ENABLE_ISOLATED_STORAGE) return; final int mountMode = getExternalStorageMountMode(uid, packageName); remountUidExternalStorage(uid, mountMode); } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index cc3b43adcacd..c2b35c1871c8 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -96,6 +96,7 @@ public class Watchdog extends Thread { "media.extractor", // system/bin/mediaextractor "media.metrics", // system/bin/mediametrics "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service + "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec "com.android.bluetooth", // Bluetooth service "/system/bin/statsd", // Stats daemon }; @@ -108,6 +109,7 @@ public class Watchdog extends Thread { "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.health@2.0::IHealth", + "android.hardware.media.c2@1.0::IComponentStore", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", "android.hardware.sensors@1.0::ISensors", diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 0271354b3fff..24f8fc26ae22 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -3830,8 +3830,11 @@ public final class ActiveServices { void serviceTimeout(ProcessRecord proc) { String anrMessage = null; - synchronized(mAm) { + if (proc.isDebugging()) { + // The app's being debugged, ignore timeout. + return; + } if (proc.executingServices.size() == 0 || proc.thread == null) { return; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0b602829d8d0..6da7f5fc98d8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7894,8 +7894,9 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); if (!isDebuggable) { - if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { - throw new SecurityException("Process not debuggable: " + app.packageName); + if (!app.isProfileableByShell()) { + throw new SecurityException("Process not debuggable, " + + "and not profileable by shell: " + app.packageName); } } mProfileData.setProfileApp(processName); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 236797b57556..525531635df6 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -407,6 +407,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { SynchronousResultReceiver wifiReceiver = null; SynchronousResultReceiver bluetoothReceiver = null; SynchronousResultReceiver modemReceiver = null; + boolean railUpdated = false; if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { // We were asked to fetch WiFi data. @@ -426,6 +427,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // Oh well. } } + synchronized (mStats) { + mStats.updateRailStatsLocked(); + } + railUpdated = true; } if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) { @@ -447,6 +452,11 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { modemReceiver = new SynchronousResultReceiver("telephony"); mTelephony.requestModemActivityInfo(modemReceiver); } + if (!railUpdated) { + synchronized (mStats) { + mStats.updateRailStatsLocked(); + } + } } final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); @@ -477,10 +487,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateRpmStatsLocked(); } - if ((updateFlags & UPDATE_RAIL) != 0) { - mStats.updateRailStatsLocked(); - } - if (bluetoothInfo != null) { if (bluetoothInfo.isValid()) { mStats.updateBluetoothStateLocked(bluetoothInfo); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 1bcc4c8a6d08..a93f2187db08 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3172,15 +3172,28 @@ public final class ProcessList { @GuardedBy("mService") void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) { + boolean foundProcess = false; for (int i = mLruProcesses.size() - 1; i >= 0; i--) { ProcessRecord r = mLruProcesses.get(i); if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) { try { + for (int index = packages.length - 1; index >= 0 && !foundProcess; index--) { + if (packages[index].equals(r.info.packageName)) { + foundProcess = true; + } + } r.thread.dispatchPackageBroadcast(cmd, packages); } catch (RemoteException ex) { } } } + + if (!foundProcess) { + try { + AppGlobals.getPackageManager().notifyPackagesReplacedReceived(packages); + } catch (RemoteException ignored) { + } + } } /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */ diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 49930c1f4d62..e891e6e450ba 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1557,7 +1557,7 @@ class ProcessRecord implements WindowProcessListener { mService.mBatteryStatsService.noteProcessAnr(processName, uid); } - if (isSilentAnr()) { + if (isSilentAnr() && !isDebugging()) { kill("bg anr", true); return; } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 99380c94b869..19c3a71810fb 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -132,16 +132,20 @@ public class SettingsToPropertiesMapper { } for (String deviceConfigScope : mDeviceConfigScopes) { - DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.addOnPropertiesChangedListener( deviceConfigScope, AsyncTask.THREAD_POOL_EXECUTOR, - (String scope, String name, String value) -> { - String propertyName = makePropertyName(scope, name); - if (propertyName == null) { - log("unable to construct system property for " + scope + "/" + name); - return; + (DeviceConfig.Properties properties) -> { + String scope = properties.getNamespace(); + for (String key : properties.getKeyset()) { + String propertyName = makePropertyName(scope, key); + if (propertyName == null) { + log("unable to construct system property for " + scope + "/" + + key); + return; + } + setProperty(propertyName, properties.getString(key, null)); } - setProperty(propertyName, value); }); } } diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index f1984646586e..d56cb7914b9a 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -46,26 +46,12 @@ ], "postsubmit": [ { - "name": "CtsActivityManagerDeviceTestCases" - }, - { - "name": "CtsActivityManagerDeviceSdk25TestCases" - }, - { "name": "FrameworksServicesTests", "options": [ { "include-filter": "com.android.server.am." } ] - }, - { - "name": "WmTests", - "options": [ - { - "include-filter": "com.android.server.am." - } - ] } ] } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c573332235d8..05be10068c94 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -43,7 +43,6 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; -import java.util.ArrayList; /** @hide */ /*package*/ final class AudioDeviceBroker { @@ -376,24 +375,29 @@ import java.util.ArrayList; mAudioService.postAccessoryPlugMediaUnmute(device); } - /*package*/ AudioService.VolumeStreamState getStreamState(int streamType) { - return mAudioService.getStreamState(streamType); + /*package*/ int getVssVolumeForDevice(int streamType, int device) { + return mAudioService.getVssVolumeForDevice(streamType, device); } - /*package*/ ArrayList<AudioService.SetModeDeathHandler> getSetModeDeathHandlers() { - return mAudioService.mSetModeDeathHandlers; + /*package*/ int getModeOwnerPid() { + return mAudioService.getModeOwnerPid(); } /*package*/ int getDeviceForStream(int streamType) { return mAudioService.getDeviceForStream(streamType); } - /*package*/ void setDeviceVolume(AudioService.VolumeStreamState streamState, int device) { - mAudioService.setDeviceVolume(streamState, device); + /*package*/ void postApplyVolumeOnDevice(int streamType, int device, String caller) { + mAudioService.postApplyVolumeOnDevice(streamType, device, caller); } - /*packages*/ void observeDevicesForAllStreams() { - mAudioService.observeDevicesForAllStreams(); + /*package*/ void postSetVolumeIndexOnDevice(int streamType, int vssVolIndex, int device, + String caller) { + mAudioService.postSetVolumeIndexOnDevice(streamType, vssVolIndex, device, caller); + } + + /*packages*/ void postObserveDevicesForAllStreams() { + mAudioService.postObserveDevicesForAllStreams(); } /*package*/ boolean isInCommunication() { @@ -767,7 +771,7 @@ import java.util.ArrayList; case MSG_L_SCOCLIENT_DIED: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { - mBtHelper.scoClientDied(msg.arg1); + mBtHelper.scoClientDied(msg.obj); } } break; diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 91b51b4989d8..f9dbdd5b13db 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -198,14 +198,10 @@ public final class AudioDeviceInventory { } } if (a2dpVolume != -1) { - AudioService.VolumeStreamState streamState = - mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC); - // Convert index to internal representation in VolumeStreamState - a2dpVolume = a2dpVolume * 10; - streamState.setIndex(a2dpVolume, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - "onSetA2dpSinkConnectionState"); - mDeviceBroker.setDeviceVolume( - streamState, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); + mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, + // convert index to internal representation in VolumeStreamState + a2dpVolume * 10, + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState"); } makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice), "onSetA2dpSinkConnectionState", a2dpCodec); @@ -302,14 +298,11 @@ public final class AudioDeviceInventory { if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) { // Device is connected if (a2dpVolume != -1) { - final AudioService.VolumeStreamState streamState = - mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC); - // Convert index to internal representation in VolumeStreamState - a2dpVolume = a2dpVolume * 10; - streamState.setIndex(a2dpVolume, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, + // convert index to internal representation in VolumeStreamState + a2dpVolume * 10, + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onBluetoothA2dpActiveDeviceChange"); - mDeviceBroker.setDeviceVolume( - streamState, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); } } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { if (di.mDeviceCodecFormat != a2dpCodec) { @@ -352,7 +345,7 @@ public final class AudioDeviceInventory { } } mRoutesObservers.finishBroadcast(); - mDeviceBroker.observeDevicesForAllStreams(); + mDeviceBroker.postObserveDevicesForAllStreams(); } private static final int DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG = @@ -655,8 +648,6 @@ public final class AudioDeviceInventory { int a2dpCodec) { // enable A2DP before notifying A2DP connection to avoid unnecessary processing in // audio policy manager - AudioService.VolumeStreamState streamState = - mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC); mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); @@ -727,8 +718,8 @@ public final class AudioDeviceInventory { @GuardedBy("mConnectedDevices") private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) { - final int hearingAidVolIndex = mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC) - .getIndex(AudioSystem.DEVICE_OUT_HEARING_AID); + final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC, + AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, AudioSystem.STREAM_MUSIC); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, @@ -739,9 +730,8 @@ public final class AudioDeviceInventory { new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID); - mDeviceBroker.setDeviceVolume( - mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC), - AudioSystem.DEVICE_OUT_HEARING_AID); + mDeviceBroker.postApplyVolumeOnDevice(AudioSystem.STREAM_MUSIC, + AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); setCurrentAudioRouteNameIfPossible(name); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 2d00f298e25e..d3af8f0745d7 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -258,6 +258,8 @@ public class AudioService extends IAudioService.Stub private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23; private static final int MSG_ENABLE_SURROUND_FORMATS = 24; private static final int MSG_UPDATE_RINGER_MODE = 25; + private static final int MSG_SET_DEVICE_STREAM_VOLUME = 26; + private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -274,8 +276,8 @@ public class AudioService extends IAudioService.Stub /** @see VolumeStreamState */ private VolumeStreamState[] mStreamStates; - /*package*/ VolumeStreamState getStreamState(int stream) { - return mStreamStates[stream]; + /*package*/ int getVssVolumeForDevice(int stream, int device) { + return mStreamStates[stream].getIndex(device); } private SettingsObserver mSettingsObserver; @@ -517,6 +519,10 @@ public class AudioService extends IAudioService.Stub AudioSystem.DEVICE_OUT_AUX_LINE; // Devices for which the volume is always max, no volume panel int mFullVolumeDevices = 0; + // Devices for the which use the "absolute volume" concept (framework sends audio signal + // full scale, and volume control separately) and can be used for multiple use cases reflected + // by the audio mode (e.g. media playback in MODE_NORMAL, and phone calls in MODE_IN_CALL). + int mAbsVolumeMultiModeCaseDevices = AudioSystem.DEVICE_OUT_HEARING_AID; private final boolean mMonitorRotation; @@ -2048,6 +2054,49 @@ public class AudioService extends IAudioService.Stub } } + /** + * Manage an audio mode change for audio devices that use an "absolute volume" model, + * i.e. the framework sends the full scale signal, and the actual volume for the use case + * is communicated separately. + */ + void updateAbsVolumeMultiModeDevices(int oldMode, int newMode) { + if (oldMode == newMode) { + return; + } + int streamType = AudioSystem.STREAM_MUSIC; + switch (newMode) { + case AudioSystem.MODE_IN_COMMUNICATION: + case AudioSystem.MODE_IN_CALL: + streamType = AudioSystem.STREAM_VOICE_CALL; + break; + case AudioSystem.MODE_NORMAL: + streamType = AudioSystem.STREAM_MUSIC; + break; + case AudioSystem.MODE_RINGTONE: + // not changing anything for ringtone + return; + case AudioSystem.MODE_CURRENT: + case AudioSystem.MODE_INVALID: + default: + // don't know what to do in this case, better bail + return; + } + final int device = AudioSystem.getDevicesForStream(streamType); + if ((device & mAbsVolumeMultiModeCaseDevices) == 0) { + return; + } + + // handling of specific interfaces goes here: + if ((device & mAbsVolumeMultiModeCaseDevices) == AudioSystem.DEVICE_OUT_HEARING_AID) { + final int index = getStreamVolume(streamType); + mModeLogger.log(new AudioEventLogger.StringEvent("setMode to " + + AudioSystem.modeToString(newMode) + + " causes setting HEARING_AID volume to idx:" + index + + " stream:" + AudioSystem.streamToString(streamType))); + mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + } + } + private void setStreamVolume(int streamType, int index, int flags, String callingPackage, String caller, int uid) { if (DEBUG_VOL) { @@ -2936,7 +2985,21 @@ public class AudioService extends IAudioService.Stub } - /*package*/ class SetModeDeathHandler implements IBinder.DeathRecipient { + /** + * Return the pid of the current audio mode owner + * @return 0 if nobody owns the mode + */ + /*package*/ int getModeOwnerPid() { + int modeOwnerPid = 0; + try { + modeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); + } catch (Exception e) { + // nothing to do, modeOwnerPid is not modified + } + return modeOwnerPid; + } + + private class SetModeDeathHandler implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death private int mPid; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client @@ -3023,9 +3086,9 @@ public class AudioService extends IAudioService.Stub } } - // must be called synchronized on mSetModeLock // setModeInt() returns a valid PID if the audio mode was successfully set to // any mode other than NORMAL. + @GuardedBy("mDeviceBroker.mSetModeLock") private int setModeInt(int mode, IBinder cb, int pid, String caller) { if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller=" + caller + ")"); } @@ -3047,6 +3110,7 @@ public class AudioService extends IAudioService.Stub break; } } + final int oldMode = mMode; int status = AudioSystem.AUDIO_STATUS_OK; int actualMode; do { @@ -3118,6 +3182,9 @@ public class AudioService extends IAudioService.Stub setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller); updateStreamVolumeAlias(true /*updateVolumes*/, caller); + + // change of mode may require volume to be re-applied on some devices + updateAbsVolumeMultiModeDevices(oldMode, actualMode); } return newModeOwnerPid; } @@ -4096,8 +4163,14 @@ public class AudioService extends IAudioService.Stub } } + /*package*/ void postObserveDevicesForAllStreams() { + sendMsg(mAudioHandler, + MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS, + SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, null /*obj*/, + 0 /*delay*/); + } - /*package*/ void observeDevicesForAllStreams() { + private void onObserveDevicesForAllStreams() { observeDevicesForStreams(-1); } @@ -4270,7 +4343,7 @@ public class AudioService extends IAudioService.Stub // 2 mSetModeLock // 3 mSettingsLock // 4 VolumeStreamState.class - public class VolumeStreamState { + private class VolumeStreamState { private final int mStreamType; private int mIndexMin; private int mIndexMax; @@ -4740,6 +4813,74 @@ public class AudioService extends IAudioService.Stub } } + private static final class DeviceVolumeUpdate { + final int mStreamType; + final int mDevice; + final @NonNull String mCaller; + private static final int NO_NEW_INDEX = -2049; + private final int mVssVolIndex; + + // Constructor with volume index, meant to cause this volume to be set and applied for the + // given stream type on the given device + DeviceVolumeUpdate(int streamType, int vssVolIndex, int device, @NonNull String caller) { + mStreamType = streamType; + mVssVolIndex = vssVolIndex; + mDevice = device; + mCaller = caller; + } + + // Constructor with no volume index, meant to cause re-apply of volume for the given + // stream type on the given device + DeviceVolumeUpdate(int streamType, int device, @NonNull String caller) { + mStreamType = streamType; + mVssVolIndex = NO_NEW_INDEX; + mDevice = device; + mCaller = caller; + } + + boolean hasVolumeIndex() { + return mVssVolIndex != NO_NEW_INDEX; + } + + int getVolumeIndex() throws IllegalStateException { + Preconditions.checkState(mVssVolIndex != NO_NEW_INDEX); + return mVssVolIndex; + } + } + + /*package*/ void postSetVolumeIndexOnDevice(int streamType, int vssVolIndex, int device, + String caller) { + sendMsg(mAudioHandler, + MSG_SET_DEVICE_STREAM_VOLUME, + SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, + new DeviceVolumeUpdate(streamType, vssVolIndex, device, caller), + 0 /*delay*/); + } + + /*package*/ void postApplyVolumeOnDevice(int streamType, int device, @NonNull String caller) { + sendMsg(mAudioHandler, + MSG_SET_DEVICE_STREAM_VOLUME, + SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, + new DeviceVolumeUpdate(streamType, device, caller), + 0 /*delay*/); + } + + private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) { + synchronized (VolumeStreamState.class) { + final VolumeStreamState streamState = mStreamStates[update.mStreamType]; + if (update.hasVolumeIndex()) { + final int index = update.getVolumeIndex(); + streamState.setIndex(index, update.mDevice, update.mCaller); + sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller + " dev:0x" + + Integer.toHexString(update.mDevice) + " volIdx:" + index)); + } else { + sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller + + " update vol on dev:0x" + Integer.toHexString(update.mDevice))); + } + setDeviceVolume(streamState, update.mDevice); + } + } + /*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) { final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported(); @@ -5180,6 +5321,14 @@ public class AudioService extends IAudioService.Stub case MSG_UPDATE_RINGER_MODE: onUpdateRingerModeServiceInt(); break; + + case MSG_SET_DEVICE_STREAM_VOLUME: + onSetVolumeIndexOnDevice((DeviceVolumeUpdate) msg.obj); + break; + + case MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS: + onObserveDevicesForAllStreams(); + break; } } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 332ff362392a..068c3d8a1264 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -765,8 +765,7 @@ public class BtHelper { broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client process. - int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() - ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); + final int modeOwnerPid = mDeviceBroker.getModeOwnerPid(); if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " + modeOwnerPid + " != creatorPid " + mCreatorPid); diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index 1913635f80e2..e33392d359dd 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -34,6 +34,7 @@ import android.net.IDnsResolver; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkUtils; +import android.net.ResolverParamsParcel; import android.net.Uri; import android.net.shared.PrivateDnsConfig; import android.os.Binder; @@ -311,11 +312,9 @@ public class DnsManager { public void setDnsConfigurationForNetwork( int netId, LinkProperties lp, boolean isDefaultNetwork) { - final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers()); - final String[] domainStrs = getDomainStrings(lp.getDomains()); updateParametersSettings(); - final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples }; + final ResolverParamsParcel paramsParcel = new ResolverParamsParcel(); // We only use the PrivateDnsConfig data pushed to this class instance // from ConnectivityService because it works in coordination with @@ -329,34 +328,44 @@ public class DnsManager { final boolean useTls = privateDnsCfg.useTls; final boolean strictMode = privateDnsCfg.inStrictMode(); - final String tlsHostname = strictMode ? privateDnsCfg.hostname : ""; - final String[] tlsServers = + paramsParcel.netId = netId; + paramsParcel.sampleValiditySeconds = mSampleValidity; + paramsParcel.successThreshold = mSuccessThreshold; + paramsParcel.minSamples = mMinSamples; + paramsParcel.maxSamples = mMaxSamples; + paramsParcel.servers = NetworkUtils.makeStrings(lp.getDnsServers()); + paramsParcel.domains = getDomainStrings(lp.getDomains()); + paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : ""; + paramsParcel.tlsServers = strictMode ? NetworkUtils.makeStrings( Arrays.stream(privateDnsCfg.ips) .filter((ip) -> lp.isReachable(ip)) .collect(Collectors.toList())) - : useTls ? assignedServers // Opportunistic + : useTls ? paramsParcel.servers // Opportunistic : new String[0]; // Off - + paramsParcel.tlsFingerprints = new String[0]; // Prepare to track the validation status of the DNS servers in the // resolver config when private DNS is in opportunistic or strict mode. if (useTls) { if (!mPrivateDnsValidationMap.containsKey(netId)) { mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses()); } - mPrivateDnsValidationMap.get(netId).updateTrackedDnses(tlsServers, tlsHostname); + mPrivateDnsValidationMap.get(netId).updateTrackedDnses(paramsParcel.tlsServers, + paramsParcel.tlsName); } else { mPrivateDnsValidationMap.remove(netId); } - Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)", - netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs), - Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers))); - final String[] tlsFingerprints = new String[0]; + Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, " + + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers), + Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds, + paramsParcel.successThreshold, paramsParcel.minSamples, + paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec, + paramsParcel.retryCount, paramsParcel.tlsName, + Arrays.toString(paramsParcel.tlsServers))); + try { - mDnsResolver.setResolverConfiguration( - netId, assignedServers, domainStrs, params, - tlsHostname, tlsServers, tlsFingerprints); + mDnsResolver.setResolverConfiguration(paramsParcel); } catch (RemoteException | ServiceSpecificException e) { Slog.e(TAG, "Error setting DNS configuration: " + e); return; diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java index 1820acfaf293..ccd1db496675 100644 --- a/services/core/java/com/android/server/job/controllers/QuotaController.java +++ b/services/core/java/com/android/server/job/controllers/QuotaController.java @@ -29,6 +29,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; +import android.app.AppGlobals; import android.app.IUidObserver; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; @@ -49,6 +50,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseSetArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -277,9 +279,9 @@ public final class QuotaController extends StateController { .append(", ") .append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ") .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed).append(", ") - .append("jobCountExpirationTime").append(jobCountExpirationTimeElapsed) + .append("jobCountExpirationTime=").append(jobCountExpirationTimeElapsed) .append(", ") - .append("jobCountInAllowedTime").append(jobCountInAllowedTime) + .append("jobCountInAllowedTime=").append(jobCountInAllowedTime) .toString(); } @@ -338,6 +340,9 @@ public final class QuotaController extends StateController { /** List of UIDs currently in the foreground. */ private final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); + /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */ + private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>(); + /** * List of jobs that started while the UID was in the TOP state. There will be no more than * 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is @@ -421,6 +426,22 @@ public final class QuotaController extends StateController { } }; + private final BroadcastReceiver mPackageAddedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) { + return; + } + if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + return; + } + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + synchronized (mLock) { + mUidToPackageCache.remove(uid); + } + } + }; + /** * The rolling window size for each standby bucket. Within each window, an app will have 10 * minutes to run its jobs. @@ -469,6 +490,9 @@ public final class QuotaController extends StateController { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null); + // Set up the app standby bucketing tracker UsageStatsManagerInternal usageStats = LocalServices.getService( UsageStatsManagerInternal.class); @@ -502,10 +526,15 @@ public final class QuotaController extends StateController { @Override public void prepareForExecutionLocked(JobStatus jobStatus) { - if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString()); + if (DEBUG) { + Slog.d(TAG, "Prepping for " + jobStatus.toShortString()); + } final int uid = jobStatus.getSourceUid(); if (mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_TOP) { + if (DEBUG) { + Slog.d(TAG, jobStatus.toShortString() + " is top started job"); + } mTopStartedJobs.add(jobStatus); // Top jobs won't count towards quota so there's no need to involve the Timer. return; @@ -518,7 +547,7 @@ public final class QuotaController extends StateController { timer = new Timer(uid, userId, packageName); mPkgTimers.add(userId, packageName, timer); } - timer.startTrackingJob(jobStatus); + timer.startTrackingJobLocked(jobStatus); } @Override @@ -645,7 +674,7 @@ public final class QuotaController extends StateController { if (timer != null) { if (timer.isActive()) { Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off."); - timer.dropEverything(); + timer.dropEverythingLocked(); } mPkgTimers.delete(userId, packageName); } @@ -657,6 +686,7 @@ public final class QuotaController extends StateController { } mExecutionStatsCache.delete(userId, packageName); mForegroundUids.delete(uid); + mUidToPackageCache.remove(uid); } @Override @@ -666,6 +696,7 @@ public final class QuotaController extends StateController { mTimingSessions.delete(userId); mInQuotaAlarmListeners.delete(userId); mExecutionStatsCache.delete(userId); + mUidToPackageCache.clear(); } private boolean isUidInForeground(int uid) { @@ -678,7 +709,7 @@ public final class QuotaController extends StateController { } /** @return true if the job was started while the app was in the TOP state. */ - private boolean isTopStartedJob(@NonNull final JobStatus jobStatus) { + private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) { return mTopStartedJobs.contains(jobStatus); } @@ -695,14 +726,14 @@ public final class QuotaController extends StateController { return jobStatus.getStandbyBucket(); } - private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { + @VisibleForTesting + boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { final int standbyBucket = getEffectiveStandbyBucket(jobStatus); - Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); // A job is within quota if one of the following is true: // 1. it was started while the app was in the TOP state // 2. the app is currently in the foreground // 3. the app overall is within its quota - return isTopStartedJob(jobStatus) + return isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid()) || isWithinQuotaLocked( jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket); @@ -1081,7 +1112,9 @@ public final class QuotaController extends StateController { if (earliestEndElapsed == Long.MAX_VALUE) { // Couldn't find a good time to clean up. Maybe this was called after we deleted all // timing sessions. - if (DEBUG) Slog.d(TAG, "Didn't find a time to schedule cleanup"); + if (DEBUG) { + Slog.d(TAG, "Didn't find a time to schedule cleanup"); + } return; } // Need to keep sessions for all apps up to the max period, regardless of their current @@ -1095,15 +1128,19 @@ public final class QuotaController extends StateController { mNextCleanupTimeElapsed = nextCleanupElapsed; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP, mSessionCleanupAlarmListener, mHandler); - if (DEBUG) Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed); + if (DEBUG) { + Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed); + } } private void handleNewChargingStateLocked() { final long nowElapsed = sElapsedRealtimeClock.millis(); final boolean isCharging = mChargeTracker.isCharging(); - if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging); + if (DEBUG) { + Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging); + } // Deal with Timers first. - mPkgTimers.forEach((t) -> t.onStateChanged(nowElapsed, isCharging)); + mPkgTimers.forEach((t) -> t.onStateChangedLocked(nowElapsed, isCharging)); // Now update jobs. maybeUpdateAllConstraintsLocked(); } @@ -1140,7 +1177,7 @@ public final class QuotaController extends StateController { boolean changed = false; for (int i = jobs.size() - 1; i >= 0; --i) { final JobStatus js = jobs.valueAt(i); - if (isTopStartedJob(js)) { + if (isTopStartedJobLocked(js)) { // Job was started while the app was in the TOP state so we should allow it to // finish. changed |= js.setQuotaConstraintSatisfied(true); @@ -1282,7 +1319,9 @@ public final class QuotaController extends StateController { if (!alarmListener.isWaiting() || inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed) { - if (DEBUG) Slog.d(TAG, "Scheduling start alarm for " + pkgString); + if (DEBUG) { + Slog.d(TAG, "Scheduling start alarm for " + pkgString); + } // If the next time this app will have quota is at least 3 minutes before the // alarm is supposed to go off, reschedule the alarm. mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed, @@ -1430,8 +1469,8 @@ public final class QuotaController extends StateController { mUid = uid; } - void startTrackingJob(@NonNull JobStatus jobStatus) { - if (isTopStartedJob(jobStatus)) { + void startTrackingJobLocked(@NonNull JobStatus jobStatus) { + if (isTopStartedJobLocked(jobStatus)) { // We intentionally don't pay attention to fg state changes after a TOP job has // started. if (DEBUG) { @@ -1440,27 +1479,28 @@ public final class QuotaController extends StateController { } return; } - if (DEBUG) Slog.v(TAG, "Starting to track " + jobStatus.toShortString()); - synchronized (mLock) { - // Always track jobs, even when charging. - mRunningBgJobs.add(jobStatus); - if (shouldTrackLocked()) { - mBgJobCount++; - incrementJobCount(mPkg.userId, mPkg.packageName, 1); - if (mRunningBgJobs.size() == 1) { - // Started tracking the first job. - mStartTimeElapsed = sElapsedRealtimeClock.millis(); - // Starting the timer means that all cached execution stats are now - // incorrect. - invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); - scheduleCutoff(); - } + if (DEBUG) { + Slog.v(TAG, "Starting to track " + jobStatus.toShortString()); + } + // Always track jobs, even when charging. + mRunningBgJobs.add(jobStatus); + if (shouldTrackLocked()) { + mBgJobCount++; + incrementJobCount(mPkg.userId, mPkg.packageName, 1); + if (mRunningBgJobs.size() == 1) { + // Started tracking the first job. + mStartTimeElapsed = sElapsedRealtimeClock.millis(); + // Starting the timer means that all cached execution stats are now incorrect. + invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); + scheduleCutoff(); } } } void stopTrackingJob(@NonNull JobStatus jobStatus) { - if (DEBUG) Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString()); + if (DEBUG) { + Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString()); + } synchronized (mLock) { if (mRunningBgJobs.size() == 0) { // maybeStopTrackingJobLocked can be called when an app cancels a job, so a @@ -1482,7 +1522,7 @@ public final class QuotaController extends StateController { * Stops tracking all jobs and cancels any pending alarms. This should only be called if * the Timer is not going to be used anymore. */ - void dropEverything() { + void dropEverythingLocked() { mRunningBgJobs.clear(); cancelCutoff(); } @@ -1531,25 +1571,23 @@ public final class QuotaController extends StateController { return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid); } - void onStateChanged(long nowElapsed, boolean isQuotaFree) { - synchronized (mLock) { - if (isQuotaFree) { - emitSessionLocked(nowElapsed); - } else if (shouldTrackLocked()) { - // Start timing from unplug. - if (mRunningBgJobs.size() > 0) { - mStartTimeElapsed = nowElapsed; - // NOTE: this does have the unfortunate consequence that if the device is - // repeatedly plugged in and unplugged, or an app changes foreground state - // very frequently, the job count for a package may be artificially high. - mBgJobCount = mRunningBgJobs.size(); - incrementJobCount(mPkg.userId, mPkg.packageName, mBgJobCount); - // Starting the timer means that all cached execution stats are now - // incorrect. - invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); - // Schedule cutoff since we're now actively tracking for quotas again. - scheduleCutoff(); - } + void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) { + if (isQuotaFree) { + emitSessionLocked(nowElapsed); + } else if (!isActive() && shouldTrackLocked()) { + // Start timing from unplug. + if (mRunningBgJobs.size() > 0) { + mStartTimeElapsed = nowElapsed; + // NOTE: this does have the unfortunate consequence that if the device is + // repeatedly plugged in and unplugged, or an app changes foreground state + // very frequently, the job count for a package may be artificially high. + mBgJobCount = mRunningBgJobs.size(); + incrementJobCount(mPkg.userId, mPkg.packageName, mBgJobCount); + // Starting the timer means that all cached execution stats are now + // incorrect. + invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName); + // Schedule cutoff since we're now actively tracking for quotas again. + scheduleCutoff(); } } } @@ -1604,7 +1642,6 @@ public final class QuotaController extends StateController { pw.println(js.toShortString()); } } - pw.decreaseIndent(); } @@ -1667,7 +1704,9 @@ public final class QuotaController extends StateController { @Override public void onParoleStateChanged(final boolean isParoleOn) { mInParole = isParoleOn; - if (DEBUG) Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF")); + if (DEBUG) { + Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF")); + } // Update job bookkeeping out of band. BackgroundThread.getHandler().post(() -> { synchronized (mLock) { @@ -1712,7 +1751,9 @@ public final class QuotaController extends StateController { switch (msg.what) { case MSG_REACHED_QUOTA: { Package pkg = (Package) msg.obj; - if (DEBUG) Slog.d(TAG, "Checking if " + pkg + " has reached its quota."); + if (DEBUG) { + Slog.d(TAG, "Checking if " + pkg + " has reached its quota."); + } long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId, pkg.packageName); @@ -1737,7 +1778,9 @@ public final class QuotaController extends StateController { break; } case MSG_CLEAN_UP_SESSIONS: - if (DEBUG) Slog.d(TAG, "Cleaning up timing sessions."); + if (DEBUG) { + Slog.d(TAG, "Cleaning up timing sessions."); + } deleteObsoleteSessionsLocked(); maybeScheduleCleanupAlarmLocked(); @@ -1745,7 +1788,9 @@ public final class QuotaController extends StateController { case MSG_CHECK_PACKAGE: { String packageName = (String) msg.obj; int userId = msg.arg1; - if (DEBUG) Slog.d(TAG, "Checking pkg " + string(userId, packageName)); + if (DEBUG) { + Slog.d(TAG, "Checking pkg " + string(userId, packageName)); + } if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } @@ -1767,13 +1812,28 @@ public final class QuotaController extends StateController { isQuotaFree = false; } // Update Timers first. - final int userIndex = mPkgTimers.indexOfKey(userId); - if (userIndex != -1) { - final int numPkgs = mPkgTimers.numPackagesForUser(userId); - for (int p = 0; p < numPkgs; ++p) { - Timer t = mPkgTimers.valueAt(userIndex, p); - if (t != null) { - t.onStateChanged(nowElapsed, isQuotaFree); + if (mPkgTimers.indexOfKey(userId) >= 0) { + ArraySet<String> packages = mUidToPackageCache.get(uid); + if (packages == null) { + try { + String[] pkgs = AppGlobals.getPackageManager() + .getPackagesForUid(uid); + if (pkgs != null) { + for (String pkg : pkgs) { + mUidToPackageCache.add(uid, pkg); + } + packages = mUidToPackageCache.get(uid); + } + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to get package list", e); + } + } + if (packages != null) { + for (int i = packages.size() - 1; i >= 0; --i) { + Timer t = mPkgTimers.get(userId, packages.valueAt(i)); + if (t != null) { + t.onStateChangedLocked(nowElapsed, isQuotaFree); + } } } } @@ -1883,6 +1943,17 @@ public final class QuotaController extends StateController { pw.println(mForegroundUids.toString()); pw.println(); + pw.println("Cached UID->package map:"); + pw.increaseIndent(); + for (int i = 0; i < mUidToPackageCache.size(); ++i) { + final int uid = mUidToPackageCache.keyAt(i); + pw.print(uid); + pw.print(": "); + pw.println(mUidToPackageCache.get(uid)); + } + pw.decreaseIndent(); + pw.println(); + mTrackedJobs.forEach((jobs) -> { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); @@ -1936,6 +2007,29 @@ public final class QuotaController extends StateController { } } } + + pw.println("Cached execution stats:"); + pw.increaseIndent(); + for (int u = 0; u < mExecutionStatsCache.numUsers(); ++u) { + final int userId = mExecutionStatsCache.keyAt(u); + for (int p = 0; p < mExecutionStatsCache.numPackagesForUser(userId); ++p) { + final String pkgName = mExecutionStatsCache.keyAt(u, p); + ExecutionStats[] stats = mExecutionStatsCache.valueAt(u, p); + + pw.println(string(userId, pkgName)); + pw.increaseIndent(); + for (int i = 0; i < stats.length; ++i) { + ExecutionStats executionStats = stats[i]; + if (executionStats != null) { + pw.print(JobStatus.bucketName(i)); + pw.print(": "); + pw.println(executionStats); + } + } + pw.decreaseIndent(); + } + } + pw.decreaseIndent(); } @Override @@ -1995,6 +2089,49 @@ public final class QuotaController extends StateController { } } + ExecutionStats[] stats = mExecutionStatsCache.get(userId, pkgName); + if (stats != null) { + for (int bucketIndex = 0; bucketIndex < stats.length; ++bucketIndex) { + ExecutionStats es = stats[bucketIndex]; + if (es == null) { + continue; + } + final long esToken = proto.start( + StateControllerProto.QuotaController.PackageStats.EXECUTION_STATS); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.STANDBY_BUCKET, + bucketIndex); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.EXPIRATION_TIME_ELAPSED, + es.expirationTimeElapsed); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.WINDOW_SIZE_MS, + es.windowSizeMs); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.EXECUTION_TIME_IN_WINDOW_MS, + es.executionTimeInWindowMs); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_WINDOW, + es.bgJobCountInWindow); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.EXECUTION_TIME_IN_MAX_PERIOD_MS, + es.executionTimeInMaxPeriodMs); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_MAX_PERIOD, + es.bgJobCountInMaxPeriod); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED, + es.quotaCutoffTimeElapsed); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_EXPIRATION_TIME_ELAPSED, + es.jobCountExpirationTimeElapsed); + proto.write( + StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME, + es.jobCountInAllowedTime); + proto.end(esToken); + } + } + proto.end(psToken); } } diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java index b4b6160fadbe..88ff6e7d1366 100644 --- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java +++ b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java @@ -28,33 +28,16 @@ public class GnssCapabilitiesProvider { private static final String TAG = "GnssCapabilitiesProvider"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - // Bit masks for capabilities in {@link android.location.GnssCapabilities}. - private static final long GNSS_CAPABILITY_LOW_POWER_MODE = - 1L << GnssCapabilities.LOW_POWER_MODE; - private static final long GNSS_CAPABILITY_SATELLITE_BLACKLIST = - 1L << GnssCapabilities.SATELLITE_BLACKLIST; - private static final long GNSS_CAPABILITY_GEOFENCING = 1L << GnssCapabilities.GEOFENCING; - private static final long GNSS_CAPABILITY_MEASUREMENTS = 1L << GnssCapabilities.MEASUREMENTS; - private static final long GNSS_CAPABILITY_NAV_MESSAGES = 1L << GnssCapabilities.NAV_MESSAGES; - private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS = - 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS; - private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_LOS_SATS = - 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS; - private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = - 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH; - private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = - 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE; - private static final long GNSS_CAPABILITIES_TOP_HAL = - GNSS_CAPABILITY_LOW_POWER_MODE | GNSS_CAPABILITY_SATELLITE_BLACKLIST - | GNSS_CAPABILITY_GEOFENCING | GNSS_CAPABILITY_MEASUREMENTS - | GNSS_CAPABILITY_NAV_MESSAGES; + GnssCapabilities.LOW_POWER_MODE | GnssCapabilities.SATELLITE_BLACKLIST + | GnssCapabilities.GEOFENCING | GnssCapabilities.MEASUREMENTS + | GnssCapabilities.NAV_MESSAGES; private static final long GNSS_CAPABILITIES_SUB_HAL_MEASUREMENT_CORRECTIONS = - GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS - | GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_LOS_SATS - | GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH - | GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_REFLECTING_PLANE; + GnssCapabilities.MEASUREMENT_CORRECTIONS + | GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS + | GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH + | GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE; // Capabilities in {@link android.location.GnssCapabilities} supported by GNSS chipset. @GuardedBy("this") @@ -79,20 +62,20 @@ public class GnssCapabilitiesProvider { long gnssCapabilities = 0; if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_LOW_POWER_MODE)) { - gnssCapabilities |= GNSS_CAPABILITY_LOW_POWER_MODE; + gnssCapabilities |= GnssCapabilities.LOW_POWER_MODE; } if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_SATELLITE_BLACKLIST)) { - gnssCapabilities |= GNSS_CAPABILITY_SATELLITE_BLACKLIST; + gnssCapabilities |= GnssCapabilities.SATELLITE_BLACKLIST; } if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_GEOFENCING)) { - gnssCapabilities |= GNSS_CAPABILITY_GEOFENCING; + gnssCapabilities |= GnssCapabilities.GEOFENCING; } if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS)) { - gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENTS; + gnssCapabilities |= GnssCapabilities.MEASUREMENTS; } if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) { - gnssCapabilities |= GNSS_CAPABILITY_NAV_MESSAGES; + gnssCapabilities |= GnssCapabilities.NAV_MESSAGES; } synchronized (this) { @@ -110,18 +93,18 @@ public class GnssCapabilitiesProvider { * {@link android.location.GnssCapabilities}. */ void setSubHalMeasurementCorrectionsCapabilities(int measurementCorrectionsCapabilities) { - long gnssCapabilities = GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS; + long gnssCapabilities = GnssCapabilities.MEASUREMENT_CORRECTIONS; if (hasCapability(measurementCorrectionsCapabilities, GnssMeasurementCorrectionsProvider.CAPABILITY_LOS_SATS)) { - gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_LOS_SATS; + gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS; } if (hasCapability(measurementCorrectionsCapabilities, GnssMeasurementCorrectionsProvider.CAPABILITY_EXCESS_PATH_LENGTH)) { - gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH; + gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH; } if (hasCapability(measurementCorrectionsCapabilities, GnssMeasurementCorrectionsProvider.CAPABILITY_REFLECTING_PLANE)) { - gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_REFLECTING_PLANE; + gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE; } synchronized (this) { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 96fc6ec5907c..d93dddfcffb8 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -66,6 +66,7 @@ import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.util.Log; import android.util.StatsLog; +import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; @@ -332,9 +333,16 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // true if low power mode for the GNSS chipset is part of the latest request. private boolean mLowPowerMode = false; - // true if we started navigation + // true if we started navigation in the HAL, only change value of this in setStarted private boolean mStarted; + // for logging of latest change, and warning of ongoing location after a stop + private long mStartedChangedElapsedRealtime; + + // threshold for delay in GNSS engine turning off before warning & error + private static final long LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS = 2 * 1000; + private static final long LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS = 15 * 1000; + // capabilities reported through the top level IGnssCallback.hal private volatile int mTopHalCapabilities; @@ -927,6 +935,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssMeasurementsProvider.onGpsEnabledChanged(); mGnssNavigationMessageProvider.onGpsEnabledChanged(); mGnssBatchingProvider.enable(); + if (mGnssVisibilityControl != null) { + mGnssVisibilityControl.onGpsEnabledChanged(mEnabled); + } } else { mEnabled = false; Log.w(TAG, "Failed to enable location provider"); @@ -943,6 +954,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mAlarmManager.cancel(mWakeupIntent); mAlarmManager.cancel(mTimeoutIntent); + if (mGnssVisibilityControl != null) { + mGnssVisibilityControl.onGpsEnabledChanged(mEnabled); + } mGnssBatchingProvider.disable(); // do this before releasing wakelock native_cleanup(); @@ -1192,7 +1206,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements if (DEBUG) Log.d(TAG, "startNavigating"); mTimeToFirstFix = 0; mLastFixTime = 0; - mStarted = true; + setStarted(true); mPositionMode = GPS_POSITION_MODE_STANDALONE; // Notify about suppressed output, if speed limit was previously exceeded. // Elsewhere, we check again with every speed output reported. @@ -1230,12 +1244,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mLowPowerMode = mProviderRequest.lowPowerMode; if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC, interval, 0, 0, mLowPowerMode)) { - mStarted = false; + setStarted(false); Log.e(TAG, "set_position_mode failed in startNavigating()"); return; } if (!native_start()) { - mStarted = false; + setStarted(false); Log.e(TAG, "native_start failed in startNavigating()"); return; } @@ -1258,7 +1272,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private void stopNavigating() { if (DEBUG) Log.d(TAG, "stopNavigating"); if (mStarted) { - mStarted = false; + setStarted(false); native_stop(); mLastFixTime = 0; // native_stop() may reset the position mode in hardware. @@ -1270,6 +1284,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } + private void setStarted(boolean started) { + if (mStarted != started) { + mStarted = started; + mStartedChangedElapsedRealtime = SystemClock.elapsedRealtime(); + } + } + private void hibernate() { // stop GPS until our next fix interval arrives stopNavigating(); @@ -1319,6 +1340,21 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes); } } + } else { + // Warn or error about long delayed GNSS engine shutdown as this generally wastes + // power and sends location when not expected. + long locationAfterStartedFalseMillis = + SystemClock.elapsedRealtime() - mStartedChangedElapsedRealtime; + if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS) { + String logMessage = "Unexpected GNSS Location report " + + TimeUtils.formatDuration(locationAfterStartedFalseMillis) + + " after location turned off"; + if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS) { + Log.e(TAG, logMessage); + } else { + Log.w(TAG, logMessage); + } + } } mLastFixTime = SystemClock.elapsedRealtime(); @@ -1538,7 +1574,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private void restartLocationRequest() { if (DEBUG) Log.d(TAG, "restartLocationRequest"); - mStarted = false; + setStarted(false); updateRequirements(); } @@ -2152,7 +2188,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { StringBuilder s = new StringBuilder(); - s.append(" mStarted=").append(mStarted).append('\n'); + s.append(" mStarted=").append(mStarted).append(" (changed "); + TimeUtils.formatDuration(SystemClock.elapsedRealtime() + - mStartedChangedElapsedRealtime, s); + s.append(" ago)").append('\n'); s.append(" mFixInterval=").append(mFixInterval).append('\n'); s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n'); s.append(" mGnssMeasurementsProvider.isRegistered()=") @@ -2176,7 +2215,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements s.append("MEASUREMENT_CORRECTIONS "); } s.append(")\n"); - if (mGnssMeasurementCorrectionsProvider.isAvailableInPlatform()) { + if (hasCapability(GPS_CAPABILITY_MEASUREMENT_CORRECTIONS)) { s.append(" SubHal=MEASUREMENT_CORRECTIONS["); s.append(mGnssMeasurementCorrectionsProvider.toStringCapabilities()); s.append("]\n"); diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java index 24ee389a585b..8391f9d1609a 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -23,13 +23,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.database.ContentObserver; -import android.location.LocationManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; -import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -49,18 +46,14 @@ class GnssVisibilityControl { private static final String TAG = "GnssVisibilityControl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - // Constants related to non-framework (NFW) location access permission proxy apps. - private static final String NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX = - ".NonFrameworkLocationAccessActivity"; - private static final String NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX = - ".intent.action.NON_FRAMEWORK_LOCATION_ACCESS"; - private static final String NFW_INTENT_TYPE = "text/plain"; - private static final String LOCATION_PERMISSION_NAME = "android.permission.ACCESS_FINE_LOCATION"; private static final String[] NO_LOCATION_ENABLED_PROXY_APPS = new String[0]; + // Max wait time for synchronous method onGpsEnabledChanged() to run. + private static final long ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS = 3 * 1000; + // Wakelocks private static final String WAKELOCK_KEY = TAG; private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; @@ -72,7 +65,7 @@ class GnssVisibilityControl { private final Handler mHandler; private final Context mContext; - private boolean mIsDeviceLocationSettingsEnabled; + private boolean mIsGpsEnabled; // Number of non-framework location access proxy apps is expected to be small (< 5). private static final int ARRAY_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7; @@ -95,6 +88,30 @@ class GnssVisibilityControl { runOnHandler(this::handleInitialize); } + void onGpsEnabledChanged(boolean isEnabled) { + // The GnssLocationProvider's methods: handleEnable() calls this method after native_init() + // and handleDisable() calls this method before native_cleanup(). This method must be + // executed synchronously so that the NFW location access permissions are disabled in + // the HAL before native_cleanup() method is called. + // + // NOTE: Since improper use of runWithScissors() method can result in deadlocks, the method + // doc recommends limiting its use to cases where some initialization steps need to be + // executed in sequence before continuing which fits this scenario. + if (mHandler.runWithScissors(() -> handleGpsEnabledChanged(isEnabled), + ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS)) { + return; + } + + // After timeout, the method remains posted in the queue and hence future enable/disable + // calls to this method will all get executed in the correct sequence. But this timeout + // situation should not even arise because runWithScissors() will run in the caller's + // thread without blocking as it is the same thread as mHandler's thread. + if (!isEnabled) { + Log.w(TAG, "Native call to disable non-framework location access in GNSS HAL may" + + " get executed after native_cleanup()."); + } + } + void updateProxyApps(List<String> nfwLocationAccessProxyApps) { runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps)); } @@ -110,12 +127,6 @@ class GnssVisibilityControl { private void handleInitialize() { disableNfwLocationAccess(); // Disable until config properties are loaded. listenForProxyAppsPackageUpdates(); - listenForDeviceLocationSettingsUpdate(); - mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings(); - } - - private boolean getDeviceLocationSettings() { - return mContext.getSystemService(LocationManager.class).isLocationEnabled(); } private void listenForProxyAppsPackageUpdates() { @@ -145,18 +156,6 @@ class GnssVisibilityControl { }, UserHandle.ALL, intentFilter, null, mHandler); } - private void listenForDeviceLocationSettingsUpdate() { - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), - true, - new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - handleDeviceLocationSettingsUpdated(); - } - }, UserHandle.USER_ALL); - } - private void handleProxyAppPackageUpdate(String pkgName, String action) { final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName); if (locationPermission == null) { @@ -213,22 +212,21 @@ class GnssVisibilityControl { return false; } - private void handleDeviceLocationSettingsUpdated() { - final boolean enabled = getDeviceLocationSettings(); - Log.i(TAG, "Device location settings enabled: " + enabled); + private void handleGpsEnabledChanged(boolean isEnabled) { + if (DEBUG) Log.d(TAG, "handleGpsEnabledChanged, isEnabled: " + isEnabled); - if (mIsDeviceLocationSettingsEnabled == enabled) { + if (mIsGpsEnabled == isEnabled) { return; } - mIsDeviceLocationSettingsEnabled = enabled; - if (!mIsDeviceLocationSettingsEnabled) { + mIsGpsEnabled = isEnabled; + if (!mIsGpsEnabled) { disableNfwLocationAccess(); return; } - // When device location settings was disabled, we already set the proxy app list - // to empty in GNSS HAL. Update only if the proxy app list is not empty. + // When GNSS was disabled, we already set the proxy app list to empty in GNSS HAL. + // Update only if the proxy app list is not empty. String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps(); if (locationPermissionEnabledProxyApps.length != 0) { setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps); @@ -290,6 +288,18 @@ class GnssVisibilityControl { return "<Unknown>"; } } + + private boolean isRequestAccepted() { + return mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED; + } + + private boolean isRequestAttributedToProxyApp() { + return !TextUtils.isEmpty(mProxyAppPackageName); + } + + private boolean isEmergencyRequestNotification() { + return mInEmergencyMode && !isRequestAttributedToProxyApp(); + } } private void handlePermissionsChanged(int uid) { @@ -335,7 +345,7 @@ class GnssVisibilityControl { } private void updateNfwLocationAccessProxyAppsInGnssHal() { - if (!mIsDeviceLocationSettingsEnabled) { + if (!mIsGpsEnabled) { return; // Keep non-framework location access disabled. } setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps()); @@ -378,20 +388,37 @@ class GnssVisibilityControl { private void handleNfwNotification(NfwNotification nfwNotification) { if (DEBUG) Log.d(TAG, "Non-framework location access notification: " + nfwNotification); + if (nfwNotification.isEmergencyRequestNotification()) { + handleEmergencyNfwNotification(nfwNotification); + return; + } + final String proxyAppPackageName = nfwNotification.mProxyAppPackageName; - Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get( + final Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get( proxyAppPackageName); - boolean isLocationRequestAccepted = - nfwNotification.mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED; - boolean isPermissionMismatched; - if (isLocationPermissionEnabled == null) { - isPermissionMismatched = isLocationRequestAccepted; - } else { - isPermissionMismatched = (isLocationPermissionEnabled != isLocationRequestAccepted); - } + final boolean isLocationRequestAccepted = nfwNotification.isRequestAccepted(); + final boolean isPermissionMismatched = + (isLocationPermissionEnabled == null) ? isLocationRequestAccepted + : (isLocationPermissionEnabled != isLocationRequestAccepted); logEvent(nfwNotification, isPermissionMismatched); - if (TextUtils.isEmpty(proxyAppPackageName)) { + if (!nfwNotification.isRequestAttributedToProxyApp()) { + // Handle cases where GNSS HAL implementation correctly rejected NFW location request. + // 1. GNSS HAL implementation doesn't provide location to any NFW location use cases. + // There is no Location Attribution App configured in the framework. + // 2. GNSS HAL implementation doesn't provide location to some NFW location use cases. + // Location Attribution Apps are configured only for the supported NFW location + // use cases. All other use cases which are not supported (and always rejected) by + // the GNSS HAL implementation will have proxyAppPackageName set to empty string. + if (!isLocationRequestAccepted) { + if (DEBUG) { + Log.d(TAG, "Non-framework location request rejected. ProxyAppPackageName field" + + " is not set in the notification: " + nfwNotification + ". Number of" + + " configured proxy apps: " + mProxyAppToLocationPermissions.size()); + } + return; + } + Log.e(TAG, "ProxyAppPackageName field is not set. AppOps service not notified " + "for non-framework location access notification: " + nfwNotification); return; @@ -425,6 +452,16 @@ class GnssVisibilityControl { } } + private void handleEmergencyNfwNotification(NfwNotification nfwNotification) { + boolean isPermissionMismatched = + (nfwNotification.mResponseType == NfwNotification.NFW_RESPONSE_TYPE_REJECTED); + if (isPermissionMismatched) { + Log.e(TAG, "Emergency non-framework location request incorrectly rejected." + + " Notification: " + nfwNotification); + } + logEvent(nfwNotification, isPermissionMismatched); + } + private void logEvent(NfwNotification notification, boolean isPermissionMismatched) { StatsLog.write(StatsLog.GNSS_NFW_NOTIFICATION_REPORTED, notification.mProxyAppPackageName, diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index af299cf66d3f..69efd02dea9c 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -24,10 +24,14 @@ import static android.net.NetworkStats.UID_ALL; import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import android.annotation.Nullable; +import android.net.INetd; import android.net.NetworkStats; +import android.net.util.NetdService; +import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemClock; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ProcFileReader; @@ -64,7 +68,10 @@ public class NetworkStatsFactory { private boolean mUseBpfStats; + private INetd mNetdService; + // A persistent Snapshot since device start for eBPF stats + @GuardedBy("mPersistSnapshot") private final NetworkStats mPersistSnapshot; // TODO: only do adjustments in NetworkStatsService and remove this. @@ -272,6 +279,19 @@ public class NetworkStatsFactory { return stats; } + @GuardedBy("mPersistSnapshot") + private void requestSwapActiveStatsMapLocked() throws RemoteException { + // Ask netd to do a active map stats swap. When the binder call successfully returns, + // the system server should be able to safely read and clean the inactive map + // without race problem. + if (mUseBpfStats) { + if (mNetdService == null) { + mNetdService = NetdService.getInstance(); + } + mNetdService.trafficSwapActiveStatsMap(); + } + } + // TODO: delete the lastStats parameter private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces, int limitTag, NetworkStats lastStats) throws IOException { @@ -284,15 +304,24 @@ public class NetworkStatsFactory { stats = new NetworkStats(SystemClock.elapsedRealtime(), -1); } if (mUseBpfStats) { - if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL, - null, TAG_ALL, mUseBpfStats) != 0) { - throw new IOException("Failed to parse network stats"); + synchronized (mPersistSnapshot) { + try { + requestSwapActiveStatsMapLocked(); + } catch (RemoteException e) { + throw new IOException(e); + } + // Stats are always read from the inactive map, so they must be read after the + // swap + if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL, + null, TAG_ALL, mUseBpfStats) != 0) { + throw new IOException("Failed to parse network stats"); + } + mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime()); + mPersistSnapshot.combineAllValues(stats); + NetworkStats result = mPersistSnapshot.clone(); + result.filter(limitUid, limitIfaces, limitTag); + return result; } - mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime()); - mPersistSnapshot.combineAllValues(stats); - NetworkStats result = mPersistSnapshot.clone(); - result.filter(limitUid, limitIfaces, limitTag); - return result; } else { if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid, limitIfaces, limitTag, mUseBpfStats) != 0) { diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index b85abd98b00f..61be1f5e559b 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -46,6 +46,7 @@ public interface NotificationDelegate { int notificationLocation); void onNotificationDirectReplied(String key); void onNotificationSettingsViewed(String key); + void onNotificationBubbleChanged(String key, boolean isBubble); /** * Notifies that smart replies and actions have been added to the UI. diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2e8e65b7aa77..9fc30ebc8cc7 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1020,6 +1020,24 @@ public class NotificationManagerService extends SystemService { } } } + + @Override + public void onNotificationBubbleChanged(String key, boolean isBubble) { + synchronized (mNotificationLock) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r != null) { + final StatusBarNotification n = r.sbn; + final int callingUid = n.getUid(); + final String pkg = n.getPackageName(); + if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid, + null /* oldEntry */)) { + r.getNotification().flags |= FLAG_BUBBLE; + } else { + r.getNotification().flags &= ~FLAG_BUBBLE; + } + } + } + } }; @VisibleForTesting @@ -2863,8 +2881,7 @@ public class NotificationManagerService extends SystemService { // Reset notification preferences if (!fromApp) { - mPreferencesHelper.onPackagesChanged( - true, UserHandle.getCallingUserId(), packages, uids); + mPreferencesHelper.clearData(packageName, uid); } handleSavePolicyFile(); @@ -4783,6 +4800,19 @@ public class NotificationManagerService extends SystemService { private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId, NotificationRecord oldRecord) { Notification notification = r.getNotification(); + if (isNotificationAppropriateToBubble(r, pkg, userId, oldRecord)) { + notification.flags |= FLAG_BUBBLE; + } else { + notification.flags &= ~FLAG_BUBBLE; + } + } + + /** + * @return whether the provided notification record is allowed to be represented as a bubble. + */ + private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId, + NotificationRecord oldRecord) { + Notification notification = r.getNotification(); // Does the app want to bubble & have permission to bubble? boolean canBubble = notification.getBubbleMetadata() != null @@ -4808,12 +4838,7 @@ public class NotificationManagerService extends SystemService { // OR something that was previously a bubble & still exists boolean bubbleUpdate = oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0; - - if (canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate)) { - notification.flags |= FLAG_BUBBLE; - } else { - notification.flags &= ~FLAG_BUBBLE; - } + return canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate); } private void doChannelWarningToast(CharSequence toastText) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 642fa7fc3ba6..9e16632bb6c4 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1717,6 +1717,23 @@ public class PreferencesHelper implements RankingConfig { } } + public void clearData(String pkg, int uid) { + synchronized (mPackagePreferences) { + PackagePreferences p = getPackagePreferencesLocked(pkg, uid); + if (p != null) { + p.channels = new ArrayMap<>(); + p.groups = new ArrayMap<>(); + p.delegate = null; + p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; + p.allowBubble = DEFAULT_ALLOW_BUBBLE; + p.importance = DEFAULT_IMPORTANCE; + p.priority = DEFAULT_PRIORITY; + p.visibility = DEFAULT_VISIBILITY; + p.showBadge = DEFAULT_SHOW_BADGE; + } + } + } + private LogMaker getChannelLog(NotificationChannel channel, String pkg) { return new LogMaker( com.android.internal.logging.nano.MetricsProto.MetricsEvent diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c4d4106804e1..a25f8c082d71 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -598,6 +598,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final String ODM_OVERLAY_DIR = "/odm/overlay"; + private static final String OEM_OVERLAY_DIR = "/oem/overlay"; + /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; static { @@ -952,6 +954,9 @@ public class PackageManagerService extends IPackageManager.Stub ActivityInfo mInstantAppInstallerActivity; final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo(); + private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>> + mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>()); + final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates = new SparseArray<>(); @@ -1319,6 +1324,11 @@ public class PackageManagerService extends IPackageManager.Stub static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; static final int ENABLE_ROLLBACK_STATUS = 21; static final int ENABLE_ROLLBACK_TIMEOUT = 22; + static final int DEFERRED_NO_KILL_POST_DELETE = 23; + static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24; + + static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; + static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds @@ -1525,6 +1535,20 @@ public class PackageManagerService extends IPackageManager.Stub Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1); } break; + case DEFERRED_NO_KILL_POST_DELETE: { + synchronized (mInstallLock) { + InstallArgs args = (InstallArgs) msg.obj; + if (args != null) { + args.doPostDeleteLI(true); + } + } + } break; + case DEFERRED_NO_KILL_INSTALL_OBSERVER: { + String packageName = (String) msg.obj; + if (packageName != null) { + notifyInstallObserver(packageName); + } + } break; case WRITE_SETTINGS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { @@ -1791,7 +1815,10 @@ public class PackageManagerService extends IPackageManager.Stub String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver) { - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED; + final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; + + if (succeeded) { // Send the removed broadcasts if (res.removedInfo != null) { res.removedInfo.sendPackageRemovedBroadcasts(killApp); @@ -1819,8 +1846,6 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionCallback); } - final boolean update = res.removedInfo != null - && res.removedInfo.removedPackage != null; final String installerPackageName = res.installerPackageName != null ? res.installerPackageName @@ -2029,11 +2054,18 @@ public class PackageManagerService extends IPackageManager.Stub getUnknownSourcesSettings()); // Remove the replaced package's older resources safely now - // We delete after a gc for applications on sdcard. - if (res.removedInfo != null && res.removedInfo.args != null) { - Runtime.getRuntime().gc(); - synchronized (mInstallLock) { - res.removedInfo.args.doPostDeleteLI(true); + InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null; + if (args != null) { + if (!killApp) { + // If we didn't kill the app, defer the deletion of code/resource files, since + // they may still be in use by the running application. This mitigates problems + // in cases where resources or code is loaded by a new Activity before + // ApplicationInfo changes have propagated to all application threads. + scheduleDeferredNoKillPostDelete(args); + } else { + synchronized (mInstallLock) { + args.doPostDeleteLI(true); + } } } else { // Force a gc to clear up things. Ask for a background one, it's fine to go on @@ -2056,18 +2088,62 @@ public class PackageManagerService extends IPackageManager.Stub } } - // If someone is watching installs - notify them + final boolean deferInstallObserver = succeeded && update && !killApp; + if (deferInstallObserver) { + scheduleDeferredNoKillInstallObserver(res, installObserver); + } else { + notifyInstallObserver(res, installObserver); + } + } + + @Override + public void notifyPackagesReplacedReceived(String[] packages) { + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + + for (String packageName : packages) { + PackageSetting setting = mSettings.mPackages.get(packageName); + if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) { + notifyInstallObserver(packageName); + } + } + } + + private void notifyInstallObserver(String packageName) { + Pair<PackageInstalledInfo, IPackageInstallObserver2> pair = + mNoKillInstallObservers.remove(packageName); + + if (pair != null) { + notifyInstallObserver(pair.first, pair.second); + } + } + + private void notifyInstallObserver(PackageInstalledInfo info, + IPackageInstallObserver2 installObserver) { if (installObserver != null) { try { - Bundle extras = extrasForInstallResult(res); - installObserver.onPackageInstalled(res.name, res.returnCode, - res.returnMsg, extras); + Bundle extras = extrasForInstallResult(info); + installObserver.onPackageInstalled(info.name, info.returnCode, + info.returnMsg, extras); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } } } + private void scheduleDeferredNoKillPostDelete(InstallArgs args) { + Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args); + mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS); + } + + private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info, + IPackageInstallObserver2 observer) { + String packageName = info.pkg.packageName; + mNoKillInstallObservers.put(packageName, Pair.create(info, observer)); + Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName); + mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS); + } + /** * Gets the type of the external storage a package is installed on. * @param packageVolume The storage volume of the package. @@ -2557,6 +2633,13 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_AS_SYSTEM | SCAN_AS_ODM, 0); + scanDirTracedLI(new File(OEM_OVERLAY_DIR), + mDefParseFlags + | PackageParser.PARSE_IS_SYSTEM_DIR, + scanFlags + | SCAN_AS_SYSTEM + | SCAN_AS_OEM, + 0); mParallelPackageParserCallback.findStaticOverlayPackages(); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 6b804df2e068..33dd48a1ac6a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -25,6 +25,8 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import android.accounts.IAccountManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.role.IRoleManager; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.IIntentReceiver; @@ -75,6 +77,7 @@ import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseInputStream; import android.os.PersistableBundle; import android.os.Process; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ShellCommand; @@ -115,6 +118,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.WeakHashMap; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -2460,19 +2464,37 @@ class PackageManagerShellCommand extends ShellCommand { } } + String pkgName; String component = getNextArg(); - ComponentName componentName = - component != null ? ComponentName.unflattenFromString(component) : null; - - if (componentName == null) { - pw.println("Error: component name not specified or invalid"); - return 1; + if (component.indexOf('/') < 0) { + // No component specified, so assume it's just a package name. + pkgName = component; + } else { + ComponentName componentName = + component != null ? ComponentName.unflattenFromString(component) : null; + if (componentName == null) { + pw.println("Error: invalid component name"); + return 1; + } + pkgName = componentName.getPackageName(); } + + final CompletableFuture<Boolean> future = new CompletableFuture<>(); + final RemoteCallback callback = new RemoteCallback(res -> future.complete(res != null)); try { - mInterface.setHomeActivity(componentName, userId); - pw.println("Success"); - return 0; + IRoleManager roleManager = android.app.role.IRoleManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.ROLE_SERVICE)); + roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, pkgName, + 0, userId, callback); + boolean success = future.get(); + if (success) { + pw.println("Success"); + return 0; + } else { + pw.println("Error: Failed to set default home."); + return 1; + } } catch (Exception e) { pw.println(e.toString()); return 1; @@ -3161,6 +3183,10 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT"); pw.println(" Set the default home activity (aka launcher)."); + pw.println(" TARGET-COMPONENT can be a package name (com.package.my) or a full"); + pw.println(" component (com.package.my/component.name). However, only the package name"); + pw.println(" matters: the actual component used will be determined automatically from"); + pw.println(" the package."); pw.println(""); pw.println(" set-installer PACKAGE INSTALLER"); pw.println(" Set installer package name"); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index db2fba97f4ac..2a9cb8998cac 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -2687,7 +2687,7 @@ public final class Settings { private void writePackageListLPrInternal(int creatingUserId) { // Only derive GIDs for active users (not dying) - final List<UserInfo> users = UserManagerService.getInstance().getUsers(true); + final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true); int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; @@ -4357,10 +4357,26 @@ public final class Settings { return pkgSetting.getHarmfulAppWarning(userId); } + /** + * Return all users on the device, including partial or dying users. + * @param userManager UserManagerService instance + * @return the list of users + */ private static List<UserInfo> getAllUsers(UserManagerService userManager) { + return getUsers(userManager, false); + } + + /** + * Return the list of users on the device. Clear the calling identity before calling into + * UserManagerService. + * @param userManager UserManagerService instance + * @param excludeDying Indicates whether to exclude any users marked for deletion. + * @return the list of users + */ + private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) { long id = Binder.clearCallingIdentity(); try { - return userManager.getUsers(false); + return userManager.getUsers(excludeDying); } catch (NullPointerException npe) { // packagemanager not yet initialized } finally { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9b4293d484bc..d624a85acb6e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -310,6 +310,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist"; static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot"; + private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800; private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) @@ -615,6 +616,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean mPerDisplayFocusEnabled = false; private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; + private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS; + private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5; @@ -782,6 +785,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this, + UserHandle.USER_ALL); updateSettings(); } @@ -1105,16 +1111,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { case SHORT_PRESS_POWER_NOTHING: break; case SHORT_PRESS_POWER_GO_TO_SLEEP: - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); + goToSleepFromPowerButton(eventTime, 0); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); + goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); - launchHomeFromHotKey(DEFAULT_DISPLAY); + if (goToSleepFromPowerButton(eventTime, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) { + launchHomeFromHotKey(DEFAULT_DISPLAY); + } break; case SHORT_PRESS_POWER_GO_HOME: shortPressPowerGoHome(); @@ -1137,6 +1143,35 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + /** + * Sends the device to sleep as a result of a power button press. + * + * @return True if the was device was sent to sleep, false if sleep was suppressed. + */ + private boolean goToSleepFromPowerButton(long eventTime, int flags) { + // Before we actually go to sleep, we check the last wakeup reason. + // If the device very recently woke up from a gesture (like user lifting their device) + // then ignore the sleep instruction. This is because users have developed + // a tendency to hit the power button immediately when they pick up their device, and we + // don't want to put the device back to sleep in those cases. + final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup(); + if (lastWakeUp != null && lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE) { + final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE, + POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS); + final long now = SystemClock.uptimeMillis(); + if (mPowerButtonSuppressionDelayMillis > 0 + && (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) { + Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: " + + (now - lastWakeUp.wakeTime) + "ms"); + return false; + } + } + + goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags); + return true; + } + private void goToSleep(long eventTime, int reason, int flags) { mRequestedOrGoingToSleep = true; mPowerManager.goToSleep(eventTime, reason, flags); @@ -1981,6 +2016,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mRingerToggleChord = Settings.Secure.getIntForUser(resolver, Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF, UserHandle.USER_CURRENT); + mPowerButtonSuppressionDelayMillis = Settings.Global.getInt(resolver, + Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE, + POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS); if (!mContext.getResources() .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) { mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF; @@ -3594,11 +3632,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } - } else if (ExtconUEventObserver.extconExists()) { + } else if (ExtconUEventObserver.extconExists() + && ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) { HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver(); plugged = observer.init(); mHDMIObserver = observer; + } else if (localLOGV) { + Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found."); } + // This dance forces the code in setHdmiPlugged to run. // Always do this so the sticky intent is stuck (to false) if there is no hdmi. mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */); @@ -5658,7 +5700,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> { private static final String HDMI_EXIST = "HDMI=1"; - private final ExtconInfo mHdmi = new ExtconInfo("hdmi"); + private static final String NAME = "hdmi"; + private final ExtconInfo mHdmi = new ExtconInfo(NAME); private boolean init() { boolean plugged = false; diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index 5e829b2d6067..a65a81263780 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -76,6 +76,12 @@ public class AttentionDetector { private final AtomicBoolean mRequested; /** + * Monotonously increasing ID for the requests sent. + */ + @VisibleForTesting + protected int mRequestId; + + /** * {@link android.service.attention.AttentionService} API timeout. */ private long mMaxAttentionApiTimeoutMillis; @@ -105,36 +111,13 @@ public class AttentionDetector { private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0); @VisibleForTesting - final AttentionCallbackInternal mCallback = new AttentionCallbackInternal() { - @Override - public void onSuccess(int result, long timestamp) { - Slog.v(TAG, "onSuccess: " + result); - if (mRequested.getAndSet(false)) { - synchronized (mLock) { - if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) { - if (DEBUG) Slog.d(TAG, "Device slept before receiving callback."); - return; - } - if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) { - mOnUserAttention.run(); - } else { - resetConsecutiveExtensionCount(); - } - } - } - } - - @Override - public void onFailure(int error) { - Slog.i(TAG, "Failed to check attention: " + error); - mRequested.set(false); - } - }; + AttentionCallbackInternalImpl mCallback; public AttentionDetector(Runnable onUserAttention, Object lock) { mOnUserAttention = onUserAttention; mLock = lock; mRequested = new AtomicBoolean(false); + mRequestId = 0; // Device starts with an awake state upon boot. mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; @@ -196,9 +179,7 @@ public class AttentionDetector { return nextScreenDimming; } else if (mRequested.get()) { if (DEBUG) { - // TODO(b/128134941): consider adding a member ID increasing counter in - // AttentionCallbackInternal to track this better. - Slog.d(TAG, "Pending attention callback, wait."); + Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait."); } return whenToCheck; } @@ -208,6 +189,8 @@ public class AttentionDetector { // This means that we must assume that the request was successful, and then cancel it // afterwards if AttentionManager couldn't deliver it. mRequested.set(true); + mRequestId++; + mCallback = new AttentionCallbackInternalImpl(mRequestId); final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback); if (!sent) { mRequested.set(false); @@ -301,4 +284,40 @@ public class AttentionDetector { pw.print(" mAttentionServiceSupported=" + isAttentionServiceSupported()); pw.print(" mRequested=" + mRequested); } + + @VisibleForTesting + final class AttentionCallbackInternalImpl extends AttentionCallbackInternal { + private final int mId; + + AttentionCallbackInternalImpl(int id) { + this.mId = id; + } + + @Override + public void onSuccess(int result, long timestamp) { + Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId); + // If we don't check for request ID it's possible to get into a loop: success leads + // to the onUserAttention(), which in turn triggers updateUserActivity(), which will + // call back onSuccess() instantaneously if there is a cached value, and circle repeats. + if (mId == mRequestId && mRequested.getAndSet(false)) { + synchronized (mLock) { + if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) { + if (DEBUG) Slog.d(TAG, "Device slept before receiving callback."); + return; + } + if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) { + mOnUserAttention.run(); + } else { + resetConsecutiveExtensionCount(); + } + } + } + } + + @Override + public void onFailure(int error) { + Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId); + mRequested.set(false); + } + } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index cfe11bfb4347..e2bbb2dc2dc5 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -52,6 +52,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.ServiceType; +import android.os.PowerManager.WakeData; import android.os.PowerManager.WakeReason; import android.os.PowerManagerInternal; import android.os.PowerSaveState; @@ -4850,6 +4851,12 @@ public final class PowerManagerService extends SystemService } } + private PowerManager.WakeData getLastWakeupInternal() { + synchronized (mLock) { + return new WakeData(mLastWakeTime, mLastWakeReason); + } + } + private final class LocalService extends PowerManagerInternal { @Override public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) { @@ -4971,5 +4978,10 @@ public final class PowerManagerService extends SystemService public boolean wasDeviceIdleFor(long ms) { return wasDeviceIdleForInternal(ms); } + + @Override + public WakeData getLastWakeup() { + return getLastWakeupInternal(); + } } } diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 654c47780f4a..0e20905db32a 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -39,7 +39,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.database.CursorWindow; -import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -201,8 +200,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C // Package is being upgraded - we're about to get ACTION_PACKAGE_ADDED return; } - AsyncTask.THREAD_POOL_EXECUTOR.execute( - () -> performInitialGrantsIfNecessaryAsync(userId)); + performInitialGrantsIfNecessaryAsync(userId); } }, UserHandle.ALL, intentFilter, null, null); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 9cbf00b85e8b..af009ecb8c3e 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1314,6 +1314,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override + public void onNotificationBubbleChanged(String key, boolean isBubble) { + enforceStatusBarService(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationBubbleChanged(key, isBubble); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { (new StatusBarShellCommand(this, mContext)).exec( diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 10afbef12623..0891ba4be66f 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -99,7 +99,7 @@ import com.android.server.LocalServices; * data for Tron, logcat, event logs and {@link android.app.WaitResult}. * * Tests: - * atest CtsActivityManagerDeviceTestCases:ActivityMetricsLoggerTests + * atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests */ class ActivityMetricsLogger { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index ee6cf541547b..0820b0dbd372 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3372,6 +3372,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (stack.inFreeformWindowingMode()) { stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } else if (stack.getParent().inFreeformWindowingMode()) { + // If the window is on a freeform display, set it to undefined. It will be + // resolved to freeform and it can adjust windowing mode when the display mode + // changes in runtime. + stack.setWindowingMode(WINDOWING_MODE_UNDEFINED); } else { stack.setWindowingMode(WINDOWING_MODE_FREEFORM); } diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index 9f7cb3d35ecb..6318486683ae 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -129,7 +129,7 @@ class AppWindowThumbnail implements Animatable { mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter( new WindowAnimationSpec(anim, position, mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(), - mAppToken.mWmService.mWindowCornerRadius), + mAppToken.getWindowCornerRadiusForAnimation()), mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index a53f85daaa25..155ab41288e9 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -21,8 +21,6 @@ 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_UNDEFINED; import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; -import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -2542,7 +2540,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree getDisplayContent().mAppTransition.canSkipFirstFrame(), appStackClipMode, true /* isAppAnimation */, - mWmService.mWindowCornerRadius), + getWindowCornerRadiusForAnimation()), mWmService.mSurfaceAnimationRunner); if (a.getZAdjustment() == Animation.ZORDER_TOP) { mNeedsZBoost = true; diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index a7a793fa8d34..767327a278bd 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -102,7 +102,9 @@ class Dimmer { } void removeSurface() { - getPendingTransaction().remove(mDimLayer); + if (mDimLayer != null && mDimLayer.isValid()) { + getPendingTransaction().remove(mDimLayer); + } mDimLayer = null; } } @@ -305,7 +307,9 @@ class Dimmer { if (!mDimState.mDimming) { if (!mDimState.mAnimateExit) { - t.remove(mDimState.mDimLayer); + if (mDimState.mDimLayer.isValid()) { + t.remove(mDimState.mDimLayer); + } } else { startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index bd874ba786ed..43d9e43343fa 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -268,6 +268,9 @@ public class DisplayPolicy { private int[] mNavigationBarHeightForRotationInCarMode = new int[4]; private int[] mNavigationBarWidthForRotationInCarMode = new int[4]; + /** See {@link #getNavigationBarFrameHeight} */ + private int[] mNavigationBarFrameHeightForRotationDefault = new int[4]; + /** Cached value of {@link ScreenShapeHelper#getWindowOutsetBottomPx} */ @Px private int mWindowOutsetBottom; @@ -286,10 +289,6 @@ public class DisplayPolicy { } }; - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - private NavigationBarExperiments mExperiments = new NavigationBarExperiments(); - // EXPERIMENT END - @GuardedBy("mHandler") private SleepToken mDreamingSleepToken; @@ -822,6 +821,10 @@ public class DisplayPolicy { (int) attrs.hideTimeoutMilliseconds, AccessibilityManager.FLAG_CONTENT_TEXT); attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; + // Toast can show with below conditions when the screen is locked. + if (canToastShowWhenLocked(callingPid)) { + attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; + } break; } @@ -832,6 +835,16 @@ public class DisplayPolicy { } /** + * @return {@code true} if the calling activity initiate toast and is visible with + * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag. + */ + boolean canToastShowWhenLocked(int callingPid) { + return mDisplayContent.forAllWindows(w -> { + return callingPid == w.mSession.mPid && w.isVisible() && w.canShowWhenLocked(); + }, true /* traverseTopToBottom */); + } + + /** * Preflight adding a window to the system. * * Currently enforces that three window types are singletons per display: @@ -1607,11 +1620,9 @@ public class DisplayPolicy { // It's a system nav bar or a portrait screen; nav bar goes on bottom. final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed final int topNavBar = cutoutSafeUnrestricted.bottom - - mExperiments.getNavigationBarFrameHeight(); + - getNavigationBarFrameHeight(rotation, uiMode); navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom); - // EXPERIMENT END displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -1634,11 +1645,7 @@ public class DisplayPolicy { // Landscape screen; nav bar goes to the right. final int left = cutoutSafeUnrestricted.right - getNavigationBarWidth(rotation, uiMode); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - final int leftNavBar = cutoutSafeUnrestricted.right - - mExperiments.getNavigationBarFrameWidth(); - navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight); - // EXPERIMENT END + navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -1661,11 +1668,7 @@ public class DisplayPolicy { // Seascape screen; nav bar goes to the left. final int right = cutoutSafeUnrestricted.left + getNavigationBarWidth(rotation, uiMode); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - final int rightNavBar = cutoutSafeUnrestricted.left - + mExperiments.getNavigationBarFrameWidth(); - navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight); - // EXPERIMENT END + navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; if (transientNavBarShowing) { mNavigationBarController.setBarShowingLw(true); @@ -1873,10 +1876,21 @@ public class DisplayPolicy { } } - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - // Offset the ime to avoid overlapping with the nav bar - mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win); - // EXPERIMENT END + // In case the navigation bar is on the bottom, we use the frame height instead of the + // regular height for the insets we send to the IME as we need some space to show + // additional buttons in SystemUI when the IME is up. + if (mNavigationBarPosition == NAV_BAR_BOTTOM) { + final int rotation = displayFrames.mRotation; + final int uimode = mService.mPolicy.getUiMode(); + final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode) + - getNavigationBarHeight(rotation, uimode); + if (navHeightOffset > 0) { + cf.bottom -= navHeightOffset; + sf.bottom -= navHeightOffset; + vf.bottom -= navHeightOffset; + dcf.bottom -= navHeightOffset; + } + } // IM dock windows always go to the bottom of the screen. attrs.gravity = Gravity.BOTTOM; @@ -2609,6 +2623,7 @@ public class DisplayPolicy { final int upsideDownRotation = displayRotation.getUpsideDownRotation(); final int landscapeRotation = displayRotation.getLandscapeRotation(); final int seascapeRotation = displayRotation.getSeascapeRotation(); + final int uiMode = mService.mPolicy.getUiMode(); if (hasStatusBar()) { mStatusBarHeightForRotation[portraitRotation] = @@ -2632,6 +2647,14 @@ public class DisplayPolicy { mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape); + // Height of the navigation bar frame when presented horizontally at bottom + mNavigationBarFrameHeightForRotationDefault[portraitRotation] = + mNavigationBarFrameHeightForRotationDefault[upsideDownRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height); + mNavigationBarFrameHeightForRotationDefault[landscapeRotation] = + mNavigationBarFrameHeightForRotationDefault[seascapeRotation] = + res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height_landscape); + // Width of the navigation bar when presented vertically along one side mNavigationBarWidthForRotationDefault[portraitRotation] = mNavigationBarWidthForRotationDefault[upsideDownRotation] = @@ -2660,16 +2683,10 @@ public class DisplayPolicy { mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset); mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough); - // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed - mExperiments.onConfigurationChanged(uiContext); - // EXPERIMENT END - - // EXPERIMENT: TODO(b/113952590): Replace with real code after experiment. - // This should calculate how much above the frame we accept gestures. Currently, - // we extend the frame to capture the gestures, so this is 0. - mBottomGestureAdditionalInset = mExperiments.getNavigationBarFrameHeight() - - mExperiments.getNavigationBarFrameHeight(); - // EXPERIMENT END + // This should calculate how much above the frame we accept gestures. + mBottomGestureAdditionalInset = Math.max(0, + res.getDimensionPixelSize(R.dimen.navigation_bar_gesture_height) + - getNavigationBarFrameHeight(portraitRotation, uiMode)); updateConfigurationAndScreenSizeDependentBehaviors(); mWindowOutsetBottom = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); @@ -2735,6 +2752,26 @@ public class DisplayPolicy { } /** + * Get the Navigation Bar Frame height. This dimension is the height of the navigation bar that + * is used for spacing to show additional buttons on the navigation bar (such as the ime + * switcher when ime is visible) while {@link #getNavigationBarHeight} is used for the visible + * height that we send to the app as content insets that can be smaller. + * <p> + * In car mode it will return the same height as {@link #getNavigationBarHeight} + * + * @param rotation specifies rotation to return dimension from + * @param uiMode to determine if in car mode + * @return navigation bar frame height + */ + private int getNavigationBarFrameHeight(int rotation, int uiMode) { + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarHeightForRotationInCarMode[rotation]; + } else { + return mNavigationBarFrameHeightForRotationDefault[rotation]; + } + } + + /** * Return the display height available after excluding any screen * decorations that could never be removed in Honeycomb. That is, system bar or * button bar. diff --git a/services/core/java/com/android/server/wm/NavigationBarExperiments.java b/services/core/java/com/android/server/wm/NavigationBarExperiments.java deleted file mode 100644 index bb3ff5ecc6fa..000000000000 --- a/services/core/java/com/android/server/wm/NavigationBarExperiments.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; - -import android.content.Context; -import android.graphics.Rect; - -/** - * This class acts as a proxy for Navigation Bar experiments enabled with custom overlays - * {@see OverlayManagerService}. By default with no overlays, this class will essentially do nothing - * and pass the original resource data back. By default the navigation bar height/width is the same - * as the frame height/width and therefore any offsets calculated will cancel out and do nothing. - * TODO(b/113952590): Remove class once experiment in bug is completed - */ -public class NavigationBarExperiments { - - private int mNavigationBarHeight; - private int mNavigationBarWidth; - - /** - * This represents the height of the navigation bar buttons. With no experiments or overlays - * enabled, the frame height is the same as the normal navigation bar height. - */ - private int mNavigationBarFrameHeight; - - /** - * This represents the width of the navigation bar buttons. With no experiments or overlays - * enabled, the frame width is the same as the normal navigation bar width. - */ - private int mNavigationBarFrameWidth; - - /** - * Call when configuration change to refresh resource dimensions - * @param systemUiContext to get the resource values - */ - public void onConfigurationChanged(Context systemUiContext) { - // Cache all the values again - mNavigationBarHeight = systemUiContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height); - mNavigationBarWidth = systemUiContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_width); - mNavigationBarFrameHeight = systemUiContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_frame_height); - mNavigationBarFrameWidth = systemUiContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_frame_width); - } - - public int getNavigationBarHeight() { - return mNavigationBarHeight; - } - - public int getNavigationBarWidth() { - return mNavigationBarWidth; - } - - public int getNavigationBarFrameHeight() { - return mNavigationBarFrameHeight; - } - - public int getNavigationBarFrameWidth() { - return mNavigationBarFrameWidth; - } - - /** - * If navigation frame width/height is different than navigation bar width/height then only - * offset the ime's and home activity's window rects depending on the navigation bar position to - * add a gap where the navigation bar would have been drawn. With no experiments or overlays - * enabled, the height/width is the same as the frame height/width and the offsets calculated - * will be 0 and this function will do nothing. - * @param navPosition position of navigation bar (left, right or bottom) - * @param w the window that is being offset by experiment - */ - public void offsetWindowFramesForNavBar(int navPosition, WindowState w) { - if (w.getAttrs().type != TYPE_INPUT_METHOD) { - return; - } - - final WindowFrames windowFrames = w.getWindowFrames(); - final Rect cf = windowFrames.mContentFrame; - switch (navPosition) { - case NAV_BAR_BOTTOM: - int navHeight = getNavigationBarFrameHeight() - getNavigationBarHeight(); - if (navHeight > 0) { - cf.bottom -= navHeight; - windowFrames.mStableFrame.bottom -= navHeight; - } - break; - case NAV_BAR_LEFT: - case NAV_BAR_RIGHT: - int navWidth = getNavigationBarFrameWidth() - getNavigationBarWidth(); - if (navWidth > 0) { - if (navPosition == NAV_BAR_LEFT) { - cf.left += navWidth; - } else { - cf.right -= navWidth; - } - } - break; - } - } -} diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 904c50348b2c..14585c531203 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -274,7 +274,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } // Even though we want to keep original bounds, we still don't want it to stomp on // an existing task. - adjustBoundsToAvoidConflict(display, outParams.mBounds); + adjustBoundsToAvoidConflictInDisplay(display, outParams.mBounds); } } else { if (source != null && source.inFreeformWindowingMode() @@ -534,7 +534,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } // Lastly we adjust bounds to avoid conflicts with other tasks as much as possible. - adjustBoundsToAvoidConflict(display, inOutBounds); + adjustBoundsToAvoidConflictInDisplay(display, inOutBounds); } private int convertOrientationToScreenOrientation(int orientation) { @@ -678,16 +678,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { * @param display the display which tasks are to check * @param inOutBounds the bounds used to input initial bounds and output result bounds */ - private void adjustBoundsToAvoidConflict(@NonNull ActivityDisplay display, + private void adjustBoundsToAvoidConflictInDisplay(@NonNull ActivityDisplay display, @NonNull Rect inOutBounds) { - final Rect displayBounds = display.getBounds(); - if (!displayBounds.contains(inOutBounds)) { - // The initial bounds are already out of display. The scanning algorithm below doesn't - // work so well with them. - return; - } - - final List<TaskRecord> tasksToCheck = new ArrayList<>(); + final List<Rect> taskBoundsToCheck = new ArrayList<>(); for (int i = 0; i < display.getChildCount(); ++i) { final ActivityStack stack = display.getChildAt(i); if (!stack.inFreeformWindowingMode()) { @@ -695,11 +688,35 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } for (int j = 0; j < stack.getChildCount(); ++j) { - tasksToCheck.add(stack.getChildAt(j)); + taskBoundsToCheck.add(stack.getChildAt(j).getBounds()); } } + adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds); + } + + /** + * Adjusts input bounds to avoid conflict with provided display bounds and list of tasks bounds + * for the display. + * + * Scans the bounds in directions to find a candidate location that does not conflict with the + * provided list of task bounds. If starting bounds are outside the display bounds or if no + * suitable candidate bounds are found, the method returns the input bounds. + * + * @param displayBounds display bounds used to restrict the candidate bounds + * @param taskBoundsToCheck list of task bounds to check for conflict + * @param inOutBounds the bounds used to input initial bounds and output result bounds + */ + @VisibleForTesting + void adjustBoundsToAvoidConflict(@NonNull Rect displayBounds, + @NonNull List<Rect> taskBoundsToCheck, + @NonNull Rect inOutBounds) { + if (!displayBounds.contains(inOutBounds)) { + // The initial bounds are already out of display. The scanning algorithm below doesn't + // work so well with them. + return; + } - if (!boundsConflict(tasksToCheck, inOutBounds)) { + if (!boundsConflict(taskBoundsToCheck, inOutBounds)) { // Current proposal doesn't conflict with any task. Early return to avoid unnecessary // calculation. return; @@ -713,11 +730,13 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } mTmpBounds.set(inOutBounds); - while (boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) { + while (boundsConflict(taskBoundsToCheck, mTmpBounds) + && displayBounds.contains(mTmpBounds)) { shiftBounds(direction, displayBounds, mTmpBounds); } - if (!boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) { + if (!boundsConflict(taskBoundsToCheck, mTmpBounds) + && displayBounds.contains(mTmpBounds)) { // Found a candidate. Just use this. inOutBounds.set(mTmpBounds); if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds); @@ -772,16 +791,16 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { mTmpDirections[1] = Gravity.TOP | Gravity.LEFT; } - private boolean boundsConflict(@NonNull List<TaskRecord> tasks, @NonNull Rect bounds) { - for (TaskRecord task : tasks) { - final Rect taskBounds = task.getBounds(); - final boolean leftClose = Math.abs(taskBounds.left - bounds.left) + private boolean boundsConflict(@NonNull List<Rect> taskBoundsToCheck, + @NonNull Rect candidateBounds) { + for (Rect taskBounds : taskBoundsToCheck) { + final boolean leftClose = Math.abs(taskBounds.left - candidateBounds.left) < BOUNDS_CONFLICT_THRESHOLD; - final boolean topClose = Math.abs(taskBounds.top - bounds.top) + final boolean topClose = Math.abs(taskBounds.top - candidateBounds.top) < BOUNDS_CONFLICT_THRESHOLD; - final boolean rightClose = Math.abs(taskBounds.right - bounds.right) + final boolean rightClose = Math.abs(taskBounds.right - candidateBounds.right) < BOUNDS_CONFLICT_THRESHOLD; - final boolean bottomClose = Math.abs(taskBounds.bottom - bounds.bottom) + final boolean bottomClose = Math.abs(taskBounds.bottom - candidateBounds.bottom) < BOUNDS_CONFLICT_THRESHOLD; if ((leftClose && topClose) || (leftClose && bottomClose) || (rightClose && topClose) diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index eb919eb00f0c..12b62b99b3e2 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -23,6 +23,7 @@ import static android.view.Display.INVALID_DISPLAY; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING; +import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; @@ -98,7 +99,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private final ActivityTaskManagerService mAtm; // The actual proc... may be null only if 'persistent' is true (in which case we are in the // process of launching the app) - private volatile IApplicationThread mThread; + private IApplicationThread mThread; // Currently desired scheduling class private volatile int mCurSchedGroup; // Currently computed process state @@ -192,8 +193,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mPid; } + @HotPath(caller = HotPath.PROCESS_CHANGE) public void setThread(IApplicationThread thread) { - mThread = thread; + synchronized (mAtm.mGlobalLockWithoutBoost) { + mThread = thread; + } } IApplicationThread getThread() { @@ -507,7 +511,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio continue; } ActivityRecord topActivity = task.getTopActivity(); - if (topActivity != null && topActivity.visible) { + if (topActivity == null) { + continue; + } + // If an activity has just been started it will not yet be visible, but + // is expected to be soon. We treat this as if it were already visible. + // This ensures a subsequent activity can be started even before this one + // becomes visible. + if (topActivity.visible || topActivity.isState(INITIALIZING)) { return true; } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4105487c88ca..c29b132836a5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4553,7 +4553,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked()); final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */, - mWmService.mWindowCornerRadius), + mToken.getWindowCornerRadiusForAnimation()), mWmService.mSurfaceAnimationRunner); startAnimation(mPendingTransaction, adapter); commitPendingTransaction(); diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index f0b9c62f2843..f65f0ab62f69 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -345,4 +345,8 @@ class WindowToken extends WindowContainer<WindowState> { mOwnerCanManageAppTokens); return mOwnerCanManageAppTokens && (layer > navLayer); } + + float getWindowCornerRadiusForAnimation() { + return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0; + } } diff --git a/services/net/Android.bp b/services/net/Android.bp index f73a285c1a94..d72f1cf8382b 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -3,8 +3,6 @@ aidl_interface { name: "ipmemorystore-aidl-interfaces", local_include_dir: "java", srcs: [ - // TODO: Define and use a filegroup for these files, since they're also used in - // networkstack-aidl-interfaces. This does not appear to work at the moment. "java/android/net/IIpMemoryStore.aidl", "java/android/net/IIpMemoryStoreCallbacks.aidl", "java/android/net/ipmemorystore/**/*.aidl", @@ -17,17 +15,16 @@ aidl_interface { enabled: false, }, }, - api_dir: "aidl/networkstack", + api_dir: "aidl/ipmemorystore", + versions: ["1"], } aidl_interface { name: "networkstack-aidl-interfaces", local_include_dir: "java", - include_dirs: ["frameworks/base/core/java"], // For framework parcelables. + include_dirs: ["frameworks/base/core/java"], // For framework parcelables. srcs: [ "java/android/net/DhcpResultsParcelable.aidl", - "java/android/net/IIpMemoryStore.aidl", - "java/android/net/IIpMemoryStoreCallbacks.aidl", "java/android/net/INetworkMonitor.aidl", "java/android/net/INetworkMonitorCallbacks.aidl", "java/android/net/INetworkStackConnector.aidl", @@ -41,7 +38,6 @@ aidl_interface { "java/android/net/dhcp/IDhcpServerCallbacks.aidl", "java/android/net/ip/IIpClient.aidl", "java/android/net/ip/IIpClientCallbacks.aidl", - "java/android/net/ipmemorystore/**/*.aidl", ], backend: { ndk: { @@ -52,6 +48,8 @@ aidl_interface { }, }, api_dir: "aidl/networkstack", + imports: ["ipmemorystore-aidl-interfaces"], + versions: ["1"], } java_library_static { @@ -62,7 +60,7 @@ java_library_static { "ipmemorystore-client", "netd_aidl_interface-java", "networkstack-aidl-interfaces-java", - ] + ], } java_library_static { @@ -75,7 +73,7 @@ java_library_static { ], static_libs: [ "ipmemorystore-aidl-interfaces-java", - ] + ], } filegroup { diff --git a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl new file mode 100644 index 000000000000..a8cbab26190f --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl @@ -0,0 +1,9 @@ +package android.net; +interface IIpMemoryStore { + oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener); + oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener); + oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener); + oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener); + oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener); + oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl new file mode 100644 index 000000000000..cf02c26c2fe3 --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl @@ -0,0 +1,4 @@ +package android.net; +interface IIpMemoryStoreCallbacks { + oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl new file mode 100644 index 000000000000..291dbef817e6 --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +parcelable Blob { + byte[] data; +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl new file mode 100644 index 000000000000..52f40d49abd5 --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +interface IOnBlobRetrievedListener { + oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl new file mode 100644 index 000000000000..785351435d73 --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +interface IOnL2KeyResponseListener { + oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl new file mode 100644 index 000000000000..3dd2ae6e9bab --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +interface IOnNetworkAttributesRetrievedListener { + oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl new file mode 100644 index 000000000000..46d4ecb9ed7c --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +interface IOnSameL3NetworkResponseListener { + oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl new file mode 100644 index 000000000000..54e654b80c9e --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +interface IOnStatusListener { + oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status); +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl new file mode 100644 index 000000000000..9531ea3963fb --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl @@ -0,0 +1,8 @@ +package android.net.ipmemorystore; +parcelable NetworkAttributesParcelable { + byte[] assignedV4Address; + long assignedV4AddressExpiry; + String groupHint; + android.net.ipmemorystore.Blob[] dnsAddresses; + int mtu; +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl new file mode 100644 index 000000000000..414272b49f1d --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl @@ -0,0 +1,6 @@ +package android.net.ipmemorystore; +parcelable SameL3NetworkResponseParcelable { + String l2Key1; + String l2Key2; + float confidence; +} diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl new file mode 100644 index 000000000000..92c6779b5dc0 --- /dev/null +++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl @@ -0,0 +1,4 @@ +package android.net.ipmemorystore; +parcelable StatusParcelable { + int resultCode; +} diff --git a/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 000000000000..92b5345ee221 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl @@ -0,0 +1,8 @@ +package android.net; +parcelable DhcpResultsParcelable { + android.net.StaticIpConfiguration baseConfiguration; + int leaseDuration; + int mtu; + String serverAddress; + String vendorInfo; +} diff --git a/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl new file mode 100644 index 000000000000..b19f522880ec --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl @@ -0,0 +1,17 @@ +package android.net; +interface INetworkMonitor { + oneway void start(); + oneway void launchCaptivePortalApp(); + oneway void notifyCaptivePortalAppFinished(int response); + oneway void setAcceptPartialConnectivity(); + oneway void forceReevaluation(int uid); + oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config); + oneway void notifyDnsResponse(int returnCode); + oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc); + oneway void notifyNetworkDisconnected(); + oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp); + oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc); + const int NETWORK_TEST_RESULT_VALID = 0; + const int NETWORK_TEST_RESULT_INVALID = 1; + const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; +} diff --git a/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 000000000000..ee9871ddcd15 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl @@ -0,0 +1,8 @@ +package android.net; +interface INetworkMonitorCallbacks { + oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor); + oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl); + oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config); + oneway void showProvisioningNotification(String action, String packageName); + oneway void hideProvisioningNotification(); +} diff --git a/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl new file mode 100644 index 000000000000..7da11e476c0e --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl @@ -0,0 +1,7 @@ +package android.net; +interface INetworkStackConnector { + oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb); + oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb); + oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks); + oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb); +} diff --git a/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl new file mode 100644 index 000000000000..f6ca6f7a78e2 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl @@ -0,0 +1,4 @@ +package android.net; +interface INetworkStackStatusCallback { + oneway void onStatusAvailable(int statusCode); +} diff --git a/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 000000000000..c80a78785b3b --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl @@ -0,0 +1,7 @@ +package android.net; +parcelable InitialConfigurationParcelable { + android.net.LinkAddress[] ipAddresses; + android.net.IpPrefix[] directlyConnectedRoutes; + String[] dnsServers; + String gateway; +} diff --git a/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 000000000000..2de790bb7754 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl @@ -0,0 +1,5 @@ +package android.net; +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; +} diff --git a/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 000000000000..3a6c30496fd8 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl @@ -0,0 +1,15 @@ +package android.net; +parcelable ProvisioningConfigurationParcelable { + boolean enableIPv4; + boolean enableIPv6; + boolean usingMultinetworkPolicyTracker; + boolean usingIpReachabilityMonitor; + int requestedPreDhcpActionMs; + android.net.InitialConfigurationParcelable initialConfig; + android.net.StaticIpConfiguration staticIpConfig; + android.net.apf.ApfCapabilities apfCapabilities; + int provisioningTimeoutMs; + int ipv6AddrGenMode; + android.net.Network network; + String displayName; +} diff --git a/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl new file mode 100644 index 000000000000..e121c064f7ac --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl @@ -0,0 +1,13 @@ +package android.net; +parcelable TcpKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; + int seq; + int ack; + int rcvWnd; + int rcvWndScale; + int tos; + int ttl; +} diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 000000000000..67193ae904bc --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl @@ -0,0 +1,11 @@ +package android.net.dhcp; +parcelable DhcpServingParamsParcel { + int serverAddr; + int serverAddrPrefixLength; + int[] defaultRouters; + int[] dnsServers; + int[] excludedAddrs; + long dhcpLeaseTimeSecs; + int linkMtu; + boolean metered; +} diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl new file mode 100644 index 000000000000..914315855496 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl @@ -0,0 +1,10 @@ +package android.net.dhcp; +interface IDhcpServer { + oneway void start(in android.net.INetworkStackStatusCallback cb); + oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb); + oneway void stop(in android.net.INetworkStackStatusCallback cb); + const int STATUS_UNKNOWN = 0; + const int STATUS_SUCCESS = 1; + const int STATUS_INVALID_ARGUMENT = 2; + const int STATUS_UNKNOWN_ERROR = 3; +} diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl new file mode 100644 index 000000000000..dcc4489d52a6 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl @@ -0,0 +1,4 @@ +package android.net.dhcp; +interface IDhcpServerCallbacks { + oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server); +} diff --git a/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl new file mode 100644 index 000000000000..95a15742a684 --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl @@ -0,0 +1,14 @@ +package android.net.ip; +interface IIpClient { + oneway void completedPreDhcpAction(); + oneway void confirmConfiguration(); + oneway void readPacketFilterComplete(in byte[] data); + oneway void shutdown(); + oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req); + oneway void stop(); + oneway void setTcpBufferSizes(in String tcpBufferSizes); + oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo); + oneway void setMulticastFilter(boolean enabled); + oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt); + oneway void removeKeepalivePacketFilter(int slot); +} diff --git a/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl new file mode 100644 index 000000000000..d6bc8089a0be --- /dev/null +++ b/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl @@ -0,0 +1,16 @@ +package android.net.ip; +interface IIpClientCallbacks { + oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient); + oneway void onPreDhcpAction(); + oneway void onPostDhcpAction(); + oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults); + oneway void onProvisioningSuccess(in android.net.LinkProperties newLp); + oneway void onProvisioningFailure(in android.net.LinkProperties newLp); + oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp); + oneway void onReachabilityLost(in String logMsg); + oneway void onQuit(); + oneway void installPacketFilter(in byte[] filter); + oneway void startReadPacketFilter(); + oneway void setFallbackMulticastFilter(boolean enabled); + oneway void setNeighborDiscoveryOffload(boolean enable); +} diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java index 9248299e178d..4a115e6ec55b 100644 --- a/services/net/java/android/net/IpMemoryStore.java +++ b/services/net/java/android/net/IpMemoryStore.java @@ -41,6 +41,11 @@ public class IpMemoryStore extends IpMemoryStoreClient { public void onIpMemoryStoreFetched(final IIpMemoryStore memoryStore) { mService.complete(memoryStore); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }); } diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java index 90624e0518f8..714ade12435b 100644 --- a/services/net/java/android/net/ip/IpClientUtil.java +++ b/services/net/java/android/net/ip/IpClientUtil.java @@ -175,6 +175,11 @@ public class IpClientUtil { public void setNeighborDiscoveryOffload(boolean enable) { mCb.setNeighborDiscoveryOffload(enable); } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } /** diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java index fc1128b80499..66884c60b0bc 100644 --- a/services/net/java/android/net/ip/IpServer.java +++ b/services/net/java/android/net/ip/IpServer.java @@ -277,6 +277,11 @@ public class IpServer extends StateMachine { } public abstract void callback(int statusCode); + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } } private class DhcpServerCallbacksImpl extends DhcpServerCallbacks { diff --git a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java index 22978a262a11..a17483a84e78 100644 --- a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java +++ b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java @@ -40,6 +40,11 @@ public interface OnBlobRetrievedListener { listener.onBlobRetrieved(new Status(statusParcelable), l2Key, name, blob); } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } } diff --git a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java index 9e7c1c869e1a..e608aecbf498 100644 --- a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java +++ b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java @@ -40,6 +40,11 @@ public interface OnL2KeyResponseListener { listener.onL2KeyResponse(new Status(statusParcelable), l2Key); } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } } diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java index 59da26880bdd..ca6f3029d496 100644 --- a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java +++ b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java @@ -44,6 +44,11 @@ public interface OnNetworkAttributesRetrievedListener { new NetworkAttributes(networkAttributesParcelable)); } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } } diff --git a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java index 0154fd259620..67f8da81c3f2 100644 --- a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java +++ b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java @@ -43,6 +43,11 @@ public interface OnSameL3NetworkResponseListener { new SameL3NetworkResponse(sameL3NetworkResponseParcelable)); } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } } diff --git a/services/net/java/android/net/ipmemorystore/OnStatusListener.java b/services/net/java/android/net/ipmemorystore/OnStatusListener.java index 824b7b05bd5d..4262efde8843 100644 --- a/services/net/java/android/net/ipmemorystore/OnStatusListener.java +++ b/services/net/java/android/net/ipmemorystore/OnStatusListener.java @@ -39,6 +39,11 @@ public interface OnStatusListener { listener.onComplete(new Status(statusParcelable)); } } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } }; } } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 08f6a372de86..f492d13f371f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -58,6 +58,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; import android.os.BatteryManager; import android.os.BatteryManagerInternal; @@ -224,50 +225,55 @@ public class QuotaControllerTest { } private void setProcessState(int procState) { + setProcessState(procState, mSourceUid); + } + + private void setProcessState(int procState, int uid) { try { - doReturn(procState).when(mActivityMangerInternal).getUidProcessState(mSourceUid); + doReturn(procState).when(mActivityMangerInternal).getUidProcessState(uid); SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids(); spyOn(foregroundUids); - mUidObserver.onUidStateChanged(mSourceUid, procState, 0); + mUidObserver.onUidStateChanged(uid, procState, 0); if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { - verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)) - .put(eq(mSourceUid), eq(true)); - assertTrue(foregroundUids.get(mSourceUid)); + verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)) + .put(eq(uid), eq(true)); + assertTrue(foregroundUids.get(uid)); } else { - verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)).delete(eq(mSourceUid)); - assertFalse(foregroundUids.get(mSourceUid)); + verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid)); + assertFalse(foregroundUids.get(uid)); } } catch (RemoteException e) { fail("registerUidObserver threw exception: " + e.getMessage()); } } - private void setStandbyBucket(int bucketIndex) { - int bucket; + private int bucketIndexToUsageStatsBucket(int bucketIndex) { switch (bucketIndex) { case ACTIVE_INDEX: - bucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE; - break; + return UsageStatsManager.STANDBY_BUCKET_ACTIVE; case WORKING_INDEX: - bucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET; - break; + return UsageStatsManager.STANDBY_BUCKET_WORKING_SET; case FREQUENT_INDEX: - bucket = UsageStatsManager.STANDBY_BUCKET_FREQUENT; - break; + return UsageStatsManager.STANDBY_BUCKET_FREQUENT; case RARE_INDEX: - bucket = UsageStatsManager.STANDBY_BUCKET_RARE; - break; + return UsageStatsManager.STANDBY_BUCKET_RARE; default: - bucket = UsageStatsManager.STANDBY_BUCKET_NEVER; + return UsageStatsManager.STANDBY_BUCKET_NEVER; } + } + + private void setStandbyBucket(int bucketIndex) { when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID), - anyLong())).thenReturn(bucket); + anyLong())).thenReturn(bucketIndexToUsageStatsBucket(bucketIndex)); } private void setStandbyBucket(int bucketIndex, JobStatus... jobs) { setStandbyBucket(bucketIndex); for (JobStatus job : jobs) { job.setStandbyBucket(bucketIndex); + when(mUsageStatsManager.getAppStandbyBucket( + eq(job.getSourcePackageName()), eq(job.getSourceUserId()), anyLong())) + .thenReturn(bucketIndexToUsageStatsBucket(bucketIndex)); } } @@ -283,8 +289,13 @@ public class QuotaControllerTest { new ComponentName(mContext, "TestQuotaJobService")) .setMinimumLatency(Math.abs(jobId) + 1) .build(); + return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); + } + + private JobStatus createJobStatus(String testTag, String packageName, int callingUid, + JobInfo jobInfo) { JobStatus js = JobStatus.createFromJobInfo( - jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag); + jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag); // Make sure tests aren't passing just because the default bucket is likely ACTIVE. js.setStandbyBucket(FREQUENT_INDEX); return js; @@ -935,6 +946,115 @@ public class QuotaControllerTest { } @Test + public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS() { + setDischarging(); + + JobStatus jobStatus = createJobStatus( + "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS", 1); + setStandbyBucket(ACTIVE_INDEX, jobStatus); + setProcessState(ActivityManager.PROCESS_STATE_BACKUP); + + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + for (int i = 0; i < 20; ++i) { + advanceElapsedClock(SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + } + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + + advanceElapsedClock(15 * SECOND_IN_MILLIS); + + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + for (int i = 0; i < 20; ++i) { + advanceElapsedClock(SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + } + mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false); + + advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS); + + assertEquals(2, mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInAllowedTime); + assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus)); + } + + @Test + public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps() + throws Exception { + setDischarging(); + + final String unaffectedPkgName = "com.android.unaffected"; + final int unaffectedUid = 10987; + JobInfo unaffectedJobInfo = new JobInfo.Builder(1, + new ComponentName(unaffectedPkgName, "foo")) + .build(); + JobStatus unaffected = createJobStatus( + "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps", + unaffectedPkgName, unaffectedUid, unaffectedJobInfo); + setStandbyBucket(FREQUENT_INDEX, unaffected); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE, unaffectedUid); + + final String fgChangerPkgName = "com.android.foreground.changer"; + final int fgChangerUid = 10234; + JobInfo fgChangerJobInfo = new JobInfo.Builder(2, + new ComponentName(fgChangerPkgName, "foo")) + .build(); + JobStatus fgStateChanger = createJobStatus( + "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps", + fgChangerPkgName, fgChangerUid, fgChangerJobInfo); + setStandbyBucket(ACTIVE_INDEX, fgStateChanger); + setProcessState(ActivityManager.PROCESS_STATE_BACKUP, fgChangerUid); + + IPackageManager packageManager = AppGlobals.getPackageManager(); + spyOn(packageManager); + doReturn(new String[]{unaffectedPkgName}) + .when(packageManager).getPackagesForUid(unaffectedUid); + doReturn(new String[]{fgChangerPkgName}) + .when(packageManager).getPackagesForUid(fgChangerUid); + + mQuotaController.maybeStartTrackingJobLocked(unaffected, null); + mQuotaController.prepareForExecutionLocked(unaffected); + + mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); + mQuotaController.prepareForExecutionLocked(fgStateChanger); + for (int i = 0; i < 20; ++i) { + advanceElapsedClock(SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid); + } + mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false); + + advanceElapsedClock(15 * SECOND_IN_MILLIS); + + mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null); + mQuotaController.prepareForExecutionLocked(fgStateChanger); + for (int i = 0; i < 20; ++i) { + advanceElapsedClock(SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid); + } + mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false); + + mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false); + + assertTrue(mQuotaController.isWithinQuotaLocked(unaffected)); + assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger)); + assertEquals(1, + mQuotaController.getTimingSessions(SOURCE_USER_ID, unaffectedPkgName).size()); + assertEquals(42, + mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size()); + for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) { + assertEquals(42, mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, fgChangerPkgName, i).jobCountInAllowedTime); + assertEquals(1, mQuotaController.getExecutionStatsLocked( + SOURCE_USER_ID, unaffectedPkgName, i).jobCountInAllowedTime); + } + } + + @Test public void testMaybeScheduleCleanupAlarmLocked() { // No sessions saved yet. mQuotaController.maybeScheduleCleanupAlarmLocked(); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java index 3f9a57e07876..3cdadd58486f 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -19,7 +19,6 @@ package com.android.server.om; import static android.content.om.OverlayInfo.STATE_DISABLED; import static android.content.om.OverlayInfo.STATE_ENABLED; import static android.content.om.OverlayInfo.STATE_MISSING_TARGET; -import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -172,8 +171,9 @@ public class OverlayManagerServiceImplTests { mImpl.setEnabled(OVERLAY, true, USER); assertState(STATE_ENABLED, OVERLAY, USER); + // target upgrades do not change the state of the overlay beginUpgradeTargetPackage(TARGET, USER); - assertState(STATE_TARGET_IS_BEING_REPLACED, OVERLAY, USER); + assertState(STATE_ENABLED, OVERLAY, USER); endUpgradeTargetPackage(TARGET, USER); assertState(STATE_ENABLED, OVERLAY, USER); diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index 4de00f7b5565..c30a7dd8321c 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -22,6 +22,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -216,6 +218,53 @@ public class AttentionDetectorTest extends AndroidTestCase { } @Test + public void testCallbackOnSuccess_doesNotCallNonCurrentCallback() { + mAttentionDetector.mRequestId = 5; + registerAttention(); // mRequestId = 6; + mAttentionDetector.mRequestId = 55; + + mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT, + SystemClock.uptimeMillis()); + verify(mOnUserAttention, never()).run(); + } + + @Test + public void testCallbackOnSuccess_callsCallbackAfterOldCallbackCame() { + mAttentionDetector.mRequestId = 5; + registerAttention(); // mRequestId = 6; + mAttentionDetector.mRequestId = 55; + + mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT, + SystemClock.uptimeMillis()); // old callback came + mAttentionDetector.mRequestId = 6; // now back to current + mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT, + SystemClock.uptimeMillis()); + verify(mOnUserAttention).run(); + } + + @Test + public void testCallbackOnSuccess_DoesNotGoIntoInfiniteLoop() { + // Mimic real behavior + doAnswer((invocation) -> { + // Mimic a cache hit: calling onSuccess() immediately + registerAttention(); + mAttentionDetector.mRequestId++; + mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT, + SystemClock.uptimeMillis()); + return null; + }).when(mOnUserAttention).run(); + + registerAttention(); + // This test fails with literal stack overflow: + // e.g. java.lang.StackOverflowError: stack size 1039KB + mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT, + SystemClock.uptimeMillis()); + + // We don't actually get here when the test fails + verify(mOnUserAttention, atMost(1)).run(); + } + + @Test public void testCallbackOnFailure_unregistersCurrentRequestCode() { registerAttention(); mAttentionDetector.mCallback.onFailure(AttentionService.ATTENTION_FAILURE_UNKNOWN); @@ -232,7 +281,6 @@ public class AttentionDetectorTest extends AndroidTestCase { } private class TestableAttentionDetector extends AttentionDetector { - private boolean mAttentionServiceSupported; TestableAttentionDetector() { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 2d101dd87a0f..34bb0a89227a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -71,7 +71,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Activity; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.AutomaticZenRule; @@ -5047,4 +5046,112 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(), mUid + UserHandle.PER_USER_RANGE); } + + @Test + public void testNotificationBubbleChanged_false() throws Exception { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Say we're foreground + when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // First we were a bubble + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsBefore.length); + assertTrue((notifsBefore[0].getNotification().flags & FLAG_BUBBLE) != 0); + + // Notify we're not a bubble + mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false); + waitForIdle(); + + // Now we are not a bubble + StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsAfter.length); + assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0); + } + + @Test + public void testNotificationBubbleChanged_true() throws Exception { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Plain notification that has bubble metadata + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // Would be a normal notification because wouldn't have met requirements to bubble + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsBefore.length); + assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0); + + // Make the package foreground so that we're allowed to be a bubble + when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + + // Notify we are now a bubble + mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true); + waitForIdle(); + + // Make sure we are a bubble + StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsAfter.length); + assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0); + } + + @Test + public void testNotificationBubbleChanged_true_notAllowed() throws Exception { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif that is not a bubble + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // Would be a normal notification because wouldn't have met requirements to bubble + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsBefore.length); + assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0); + + // Notify we are now a bubble + mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true); + waitForIdle(); + + // We still wouldn't be a bubble because the notification didn't meet requirements + StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsAfter.length); + assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index b34bd2595287..1a06490195aa 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -1644,6 +1644,38 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testClearData() { + ArraySet<String> pkg = new ArraySet<>(); + pkg.add(PKG_O); + mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false); + mHelper.createNotificationChannelGroup( + PKG_O, UID_O, new NotificationChannelGroup("1", "bye"), true); + mHelper.lockChannelsForOEM(pkg.toArray(new String[]{})); + mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, pkg); + mHelper.setNotificationDelegate(PKG_O, UID_O, "", 1); + mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE); + mHelper.setBubblesAllowed(PKG_O, UID_O, false); + mHelper.setShowBadge(PKG_O, UID_O, false); + mHelper.setAppImportanceLocked(PKG_O, UID_O); + + mHelper.clearData(PKG_O, UID_O); + + assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O, UID_O)); + assertTrue(mHelper.areBubblesAllowed(PKG_O, UID_O)); + assertTrue(mHelper.canShowBadge(PKG_O, UID_O)); + assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); + assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); + assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size()); + assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size()); + + NotificationChannel channel = getChannel(); + mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false); + + assertTrue(channel.isImportanceLockedByCriticalDeviceFunction()); + assertTrue(channel.isImportanceLockedByOEM()); + } + + @Test public void testRecordDefaults() throws Exception { assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, UID_N_MR1)); diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index bfda2eac25c9..5136705ffbc2 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -42,6 +42,7 @@ android:testOnly="true" android:largeHeap="true"> <uses-library android:name="android.test.mock" android:required="true" /> + <uses-library android:name="android.test.runner" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" /> diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 5b32fe68feae..292a05bd966a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.when; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -78,7 +79,9 @@ public class DimmerTests extends WindowTestsBase { @Override public SurfaceControl build() { - return mock(SurfaceControl.class); + SurfaceControl mSc = mock(SurfaceControl.class); + when(mSc.isValid()).thenReturn(true); + return mSc; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 07dd93c948d1..1684f97d28e3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -22,13 +22,17 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -38,6 +42,7 @@ import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; @@ -232,4 +237,40 @@ public class DisplayPolicyTests extends WindowTestsBase { displayRotation, Surface.ROTATION_0, Surface.ROTATION_90)); } } + + @Test + public void testShouldShowToastWhenScreenLocked() { + final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); + final WindowState activity = createApplicationWindow(); + final WindowState toast = createToastWindow(); + + synchronized (mWm.mGlobalLock) { + policy.adjustWindowParamsLw( + toast, toast.mAttrs, 0 /* callingPid */, 0 /* callingUid */); + + assertTrue(policy.canToastShowWhenLocked(0 /* callingUid */)); + assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED); + } + } + + private WindowState createToastWindow() { + final WindowState win = createWindow(null, TYPE_TOAST, "Toast"); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = WRAP_CONTENT; + attrs.height = WRAP_CONTENT; + attrs.flags = FLAG_KEEP_SCREEN_ON | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE; + attrs.format = PixelFormat.TRANSLUCENT; + return win; + } + + private WindowState createApplicationWindow() { + final WindowState win = createWindow(null, TYPE_APPLICATION, "Application"); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = FLAG_SHOW_WHEN_LOCKED | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + attrs.format = PixelFormat.OPAQUE; + win.mHasSurface = true; + return win; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index cdbb12109c58..f918149e6781 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -52,6 +52,8 @@ import com.android.server.wm.LaunchParamsController.LaunchParams; import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; /** @@ -1206,6 +1208,23 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { assertEquals(new Rect(120, 0, 320, 100), mResult.mBounds); } + @Test + public void testAdjustBoundsToAvoidConflictAlwaysExits() { + Rect displayBounds = new Rect(0, 0, 40, 40); + List<Rect> existingTaskBounds = new ArrayList<>(); + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + int left = i * 5; + int top = j * 5; + existingTaskBounds.add(new Rect(left, top, left + 20, top + 20)); + } + } + Rect startingBounds = new Rect(0, 0, 20, 20); + Rect adjustedBounds = new Rect(startingBounds); + mTarget.adjustBoundsToAvoidConflict(displayBounds, existingTaskBounds, adjustedBounds); + assertEquals(startingBounds, adjustedBounds); + } + private TestActivityDisplay createNewActivityDisplay(int windowingMode) { final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); display.setWindowingMode(windowingMode); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 33d5c04a8e5c..26745a7363f1 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1199,6 +1199,80 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string"; /** + * Override the SPN Display Condition 2 integer bits (lsb). B2, B1 is the last two bits of the + * spn display condition coding. + * + * The default value -1 mean this field is not config. + * + * B1 = 0: display of registered PLMN name not required when registered PLMN is either HPLMN + * or a PLMN in the service provider PLMN list (see EF_SPDI). + * B1 = 1: display of registered PLMN name required when registered PLMN is either HPLMN or a + * PLMN in the service provider PLMN list(see EF_SPDI). + * B2 = 0: display of the service provider name is required when registered PLMN is neither + * HPLMN nor a PLMN in the service provider PLMN list(see EF_SPDI). + * B2 = 1: display of the service provider name is not required when registered PLMN is neither + * HPLMN nor a PLMN in the service provider PLMN list(see EF_SPDI). + * + * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.12 EF_SPN. + * @hide + */ + public static final String KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT = + "spn_display_condition_override_int"; + + /** + * Override the SPDI - an array of PLMN(MCC + MNC) strings. + * + * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.66 EF_SPDI. + * @hide + */ + public static final String KEY_SPDI_OVERRIDE_STRING_ARRAY = "spdi_override_string_array"; + + /** + * Override the EHPLMNs - an array of PLMN(MCC + MNC) strings. + * + * To allow provision for multiple HPLMN codes, PLMN codes that are present within this list + * shall replace the HPLMN code derived from the IMSI for PLMN selection purposes. + * + * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.84 EF_EHPLMN + * Reference: 3GPP TS 23.122 v15.6.0 Section 1.2 Equivalent HPLMN list + * @hide + */ + public static final String KEY_EHPLMN_OVERRIDE_STRING_ARRAY = "ehplmn_override_string_array"; + + /** + * Override the PNN - a string array of comma-separated alpha long and short names: + * "alpha_long1, alpha_short1". + * + * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.58 EF_PNN. + * @hide + */ + public static final String KEY_PNN_OVERRIDE_STRING_ARRAY = "pnn_override_string_array"; + + /** + * A string array of OPL records, each with comma-delimited data fields as follows: + * "plmn1,lactac_start,lactac_end,index". + * + * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.59 EF_OPL. + * @hide + */ + public static final String KEY_OPL_OVERRIDE_STRING_ARRAY = "opl_override_opl_string_array"; + + /** + * Allow ERI rules to select a carrier name display string when using 3gpp2 access technologies. + * + * @hide + */ + public static final String KEY_ALLOW_ERI_BOOL = "allow_cdma_eri_bool"; + + /** + * If true, use the carrier display name(SPN and PLMN) from the carrier display name resolver. + * + * @hide + */ + public static final String KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL = + "enable_carrier_display_name_resolver_bool"; + + /** * String to override sim country iso. * Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where * a SIM can have multiple MCC from different countries. @@ -3023,6 +3097,13 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false); sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false); sDefaults.putString(KEY_CARRIER_NAME_STRING, ""); + sDefaults.putInt(KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, -1); + sDefaults.putStringArray(KEY_SPDI_OVERRIDE_STRING_ARRAY, null); + sDefaults.putStringArray(KEY_PNN_OVERRIDE_STRING_ARRAY, null); + sDefaults.putStringArray(KEY_OPL_OVERRIDE_STRING_ARRAY, null); + sDefaults.putStringArray(KEY_EHPLMN_OVERRIDE_STRING_ARRAY, null); + sDefaults.putBoolean(KEY_ALLOW_ERI_BOOL, false); + sDefaults.putBoolean(KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL, false); sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, ""); sDefaults.putString(KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING, null); diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 2272dc9071ea..8336d1b24ddd 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -58,15 +58,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mSignalStrength; // To be removed private int mRssi; - @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O) + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mRsrp; - @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O) + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mRsrq; - @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O) + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mRssnr; - @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O) + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mCqi; - @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O) + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) private int mTimingAdvance; private int mLevel; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index d4f98743089e..32105ad52753 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2064,7 +2064,6 @@ public class SubscriptionManager { } else { logd("putPhoneIdAndSubIdExtra: no valid subs"); intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); - intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); } } @@ -2075,9 +2074,6 @@ public class SubscriptionManager { intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId); intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); - //FIXME this is using phoneId and slotIndex interchangeably - //Eventually, this should be removed as it is not the slot id - intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); } /** diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index fa6cfcb879f7..22f078fc0c05 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -635,7 +635,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu != second.getEmergencyServiceCategoryBitmask()) { return false; } - if (first.getEmergencyUrns().equals(second.getEmergencyUrns())) { + if (!first.getEmergencyUrns().equals(second.getEmergencyUrns())) { return false; } if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) { diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index d41a5d68912e..f9304f242ef3 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -39,6 +39,7 @@ import android.util.Log; import androidx.test.InstrumentationRegistry; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -774,6 +775,40 @@ public class RollbackTest { } } + @Test + @Ignore("b/120200473") + /** + * Test rollback when app is updated to its same version. + */ + public void testSameVersionUpdate() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS); + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + RollbackTestUtils.install("RollbackTestAppAv2.apk", true); + RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 2, rollback); + + RollbackTestUtils.rollback(rollback.getRollbackId()); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 2, rollback); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + /** * Test bad update automatic rollback. */ diff --git a/tests/net/Android.bp b/tests/net/Android.bp index c8ef82ec9acc..689abed19ac4 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -49,7 +49,6 @@ java_defaults { "libselinux", "libui", "libutils", - "libvintf", "libvndksupport", "libtinyxml2", "libunwindstack", @@ -64,7 +63,7 @@ java_defaults { android_test { name: "FrameworksNetTests", defaults: ["FrameworksNetTests-jni-defaults"], - srcs: ["java/**/*.java"], + srcs: ["java/**/*.java", "java/**/*.kt"], platform_apis: true, test_suites: ["device-tests"], certificate: "platform", diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 44380cce1c77..5f08a347eefd 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -131,6 +131,7 @@ import android.net.NetworkStackClient; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.ProxyInfo; +import android.net.ResolverParamsParcel; import android.net.RouteInfo; import android.net.SocketKeepalive; import android.net.UidRange; @@ -262,7 +263,8 @@ public class ConnectivityServiceTest { @Mock INetd mMockNetd; @Mock NetworkStackClient mNetworkStack; - private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class); + private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = + ArgumentCaptor.forClass(ResolverParamsParcel.class); // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods // do not go through ConnectivityService but talk to netd directly, so they don't automatically @@ -4819,16 +4821,13 @@ public class ConnectivityServiceTest { @Test public void testBasicDnsConfigurationPushed() throws Exception { setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); - ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class); // Clear any interactions that occur as a result of CS starting up. reset(mMockDnsResolver); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); waitForIdle(); - verify(mMockDnsResolver, never()).setResolverConfiguration( - anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), - eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, never()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockDnsResolver); final LinkProperties cellLp = new LinkProperties(); @@ -4846,35 +4845,33 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); waitForIdle(); // CS tells netd about the empty DNS config for this network. - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), - eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); reset(mMockDnsResolver); cellLp.addDnsServer(InetAddress.getByName("2001:db8::1")); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(1, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1")); + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(1, resolvrParams.servers.length); + assertTrue(ArrayUtils.contains(resolvrParams.servers, "2001:db8::1")); // Opportunistic mode. - assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1")); + assertTrue(ArrayUtils.contains(resolvrParams.tlsServers, "2001:db8::1")); reset(mMockDnsResolver); cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); // Opportunistic mode. - assertEquals(2, tlsServers.getValue().length); - assertTrue(ArrayUtils.containsAll(tlsServers.getValue(), + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); @@ -4887,18 +4884,16 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(TLS_SPECIFIER), eq(TLS_SERVERS), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); } @Test public void testPrivateDnsSettingsChange() throws Exception { - ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class); - // Clear any interactions that occur as a result of CS starting up. reset(mMockDnsResolver); @@ -4913,9 +4908,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); waitForIdle(); // CS tells netd about the empty DNS config for this network. - verify(mMockDnsResolver, never()).setResolverConfiguration( - anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), - eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, never()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockDnsResolver); final LinkProperties cellLp = new LinkProperties(); @@ -4936,14 +4929,14 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); waitForIdle(); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); // Opportunistic mode. - assertEquals(2, tlsServers.getValue().length); - assertTrue(ArrayUtils.containsAll(tlsServers.getValue(), + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); @@ -4958,23 +4951,23 @@ public class ConnectivityServiceTest { setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); verify(mMockDnsResolver, times(1)).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); cellNetworkCallback.assertNoCallback(); setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - anyInt(), mStringArrayCaptor.capture(), any(), any(), - eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY)); - assertEquals(2, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(), + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, new String[]{"2001:db8::1", "192.0.2.1"})); - assertEquals(2, tlsServers.getValue().length); - assertTrue(ArrayUtils.containsAll(tlsServers.getValue(), + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, new String[]{"2001:db8::1", "192.0.2.1"})); reset(mMockDnsResolver); cellNetworkCallback.assertNoCallback(); @@ -5826,9 +5819,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.sendLinkProperties(cellLp); networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - eq(cellNetId), eq(EMPTY_STRING_ARRAY), any(), any(), - eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockNetd); verifyNoMoreInteractions(mMockDnsResolver); @@ -5871,10 +5862,10 @@ public class ConnectivityServiceTest { assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0)); verify(mMockDnsResolver, times(1)).setResolverConfiguration( - eq(cellNetId), mStringArrayCaptor.capture(), any(), any(), - eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY)); - assertEquals(1, mStringArrayCaptor.getValue().length); - assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "8.8.8.8")); + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(1, resolvrParams.servers.length); + assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8")); // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked // linkproperties are cleaned up. diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt new file mode 100644 index 000000000000..f045369459c9 --- /dev/null +++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server + +import android.net.ConnectivityManager.TYPE_ETHERNET +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.ConnectivityManager.TYPE_WIMAX +import android.net.NetworkInfo.DetailedState.CONNECTED +import android.net.NetworkInfo.DetailedState.DISCONNECTED +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.server.ConnectivityService.LegacyTypeTracker +import com.android.server.connectivity.NetworkAgentInfo +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify + +const val UNSUPPORTED_TYPE = TYPE_WIMAX + +@RunWith(AndroidJUnit4::class) +@SmallTest +class LegacyTypeTrackerTest { + private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET) + + private val mMockService = mock(ConnectivityService::class.java).apply { + doReturn(false).`when`(this).isDefaultNetwork(any()) + } + private val mTracker = LegacyTypeTracker(mMockService).apply { + supportedTypes.forEach { + addSupportedType(it) + } + } + + @Test + fun testSupportedTypes() { + try { + mTracker.addSupportedType(supportedTypes[0]) + fail("Expected IllegalStateException") + } catch (expected: IllegalStateException) {} + supportedTypes.forEach { + assertTrue(mTracker.isTypeSupported(it)) + } + assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE)) + } + + @Test + fun testAddNetwork() { + val mobileNai = mock(NetworkAgentInfo::class.java) + val wifiNai = mock(NetworkAgentInfo::class.java) + mTracker.add(TYPE_MOBILE, mobileNai) + mTracker.add(TYPE_WIFI, wifiNai) + assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) + assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Make sure adding a second NAI does not change the results. + val secondMobileNai = mock(NetworkAgentInfo::class.java) + mTracker.add(TYPE_MOBILE, secondMobileNai) + assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) + assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Make sure removing a network that wasn't added for this type is a no-op. + mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) + assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) + assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Remove the top network for mobile and make sure the second one becomes the network + // of record for this type. + mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) + assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) + assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Make sure adding a network for an unsupported type does not register it. + mTracker.add(UNSUPPORTED_TYPE, mobileNai) + assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE)) + } + + @Test + fun testBroadcastOnDisconnect() { + val mobileNai1 = mock(NetworkAgentInfo::class.java) + val mobileNai2 = mock(NetworkAgentInfo::class.java) + doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1) + mTracker.add(TYPE_MOBILE, mobileNai1) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE) + reset(mMockService) + doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2) + mTracker.add(TYPE_MOBILE, mobileNai2) + verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt()) + mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE) + } +} diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp new file mode 100644 index 000000000000..ef1ad2cba804 --- /dev/null +++ b/tests/net/smoketest/Android.bp @@ -0,0 +1,17 @@ +// This test exists only because the jni_libs list for these tests is difficult to +// maintain: the test itself only depends on libnetworkstatsfactorytestjni, but the test +// fails to load that library unless *all* the dependencies of that library are explicitly +// listed in jni_libs. This means that whenever any of the dependencies changes the test +// starts failing and breaking presubmits in frameworks/base. We cannot easily put +// FrameworksNetTests into global presubmit because they are at times flaky, but this +// test is effectively empty beyond validating that the libraries load correctly, and +// thus should be stable enough to put in global presubmit. +// +// TODO: remove this hack when there is a better solution for jni_libs that includes +// dependent libraries. +android_test { + name: "FrameworksNetSmokeTests", + defaults: ["FrameworksNetTests-jni-defaults"], + srcs: ["java/SmokeTest.java"], + test_suites: ["device-tests"], +} diff --git a/tests/net/smoketest/AndroidManifest.xml b/tests/net/smoketest/AndroidManifest.xml new file mode 100644 index 000000000000..f1b9febb9f57 --- /dev/null +++ b/tests/net/smoketest/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.tests.net.smoketest"> + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.tests.net.smoketest" + android:label="Frameworks Networking Smoke Tests" /> +</manifest> diff --git a/tests/net/smoketest/AndroidTest.xml b/tests/net/smoketest/AndroidTest.xml new file mode 100644 index 000000000000..ac366e4ac544 --- /dev/null +++ b/tests/net/smoketest/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Frameworks Networking Smoke Tests."> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="FrameworksNetSmokeTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-tag" value="FrameworksNetSmokeTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.tests.net.smoketest" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/net/smoketest/java/SmokeTest.java b/tests/net/smoketest/java/SmokeTest.java new file mode 100644 index 000000000000..7d6655fde15e --- /dev/null +++ b/tests/net/smoketest/java/SmokeTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.net; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class SmokeTest { + + @Test + public void testLoadJni() { + System.loadLibrary("networkstatsfactorytestjni"); + assertEquals(0, 0x00); + } +} diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java index fd59390a87c0..70ca0882d7da 100644 --- a/wifi/java/android/net/wifi/WifiSsid.java +++ b/wifi/java/android/net/wifi/WifiSsid.java @@ -129,6 +129,8 @@ public class WifiSsid implements Parcelable { val = Integer.parseInt(asciiEncoded.substring(i, i + 2), HEX_RADIX); } catch (NumberFormatException e) { val = -1; + } catch (StringIndexOutOfBoundsException e) { + val = -1; } if (val < 0) { val = Character.digit(asciiEncoded.charAt(i), HEX_RADIX); |