Merge "Load the default graphics driver in USAPs." into qt-dev
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 d6e8ab2..8847456 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 @@
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.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 @@
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 4d22d0b..3a2e528 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 d2f63fa..b7a951c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27245,7 +27245,7 @@
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 @@
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 acbd28f..8cd722b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1714,7 +1714,6 @@
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
@@ -5662,16 +5661,16 @@
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";
}
@@ -6013,17 +6012,6 @@
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";
diff --git a/api/test-current.txt b/api/test-current.txt
index 63c8df0..3cde3f2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2265,17 +2265,6 @@
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 fa9a77a..83c034b 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 @@
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 8f9a5f8..9c9b6c7 100644
--- a/cmds/incidentd/Android.bp
+++ b/cmds/incidentd/Android.bp
@@ -59,6 +59,12 @@
"libservices",
"libutils",
"libprotobuf-cpp-lite",
+ "libcrypto",
+ "libkeystore_aidl",
+ "libkeystore_binder",
+ "libkeystore_parcelables",
+ "android.hardware.keymaster@4.0",
+ "libkeymaster4support",
],
static_libs: [
@@ -111,6 +117,8 @@
"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 @@
"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 386303b..91f0dd3 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -37,6 +37,8 @@
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 fc8caae..b599c1c4 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -87,6 +87,9 @@
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 7126322..e8fa4f4 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 @@
*/
status_t writeData(int fd);
+ sp<ProtoReader> getData() { return mData; }
+
private:
/**
* The global set of field --> required privacy level mapping.
@@ -247,8 +252,47 @@
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 @@
// 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 @@
// 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 @@
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 @@
}
// 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 @@
// 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 76b2849..d426db9 100644
--- a/cmds/incidentd/src/PrivacyFilter.h
+++ b/cmds/incidentd/src/PrivacyFilter.h
@@ -82,8 +82,14 @@
* 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 218c1b2..322b972 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -447,7 +447,8 @@
}
});
- 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 0000000..ae0a920
--- /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 0000000..27611cd
--- /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 0000000..493796d
--- /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 0000000..5b72ca8
--- /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 0000000..5c7ed24
--- /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 0000000..2250fda
--- /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 0000000..6742e03
--- /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/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e66cd31..3898328 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -39,7 +39,6 @@
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 @@
* 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 @@
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 639335e..1838bab 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -45,8 +45,7 @@
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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index abb68e7..5f3e057 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -87,8 +87,8 @@
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;
/**
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 3ffc21d..04e8cf4 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -609,16 +609,17 @@
/**
* <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 996f997..bdba77c 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -838,8 +838,10 @@
* {@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/os/Environment.java b/core/java/android/os/Environment.java
index dde1e6a..e85561c 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -1115,42 +1115,42 @@
}
/**
- * 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/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index ed44367..fd7c8739d 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.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 @@
*
* @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 @@
* @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 @@
*
* @return descriptions of the runtime permissions of the app
*/
+ @WorkerThread
public abstract @NonNull List<RuntimePermissionPresentationInfo> onGetAppPermissions(
@NonNull String packageName);
@@ -151,6 +155,7 @@
* @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 @@
*
* @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 @@
*
* @return descriptions of the users of permissions
*/
+ @WorkerThread
public abstract @NonNull List<RuntimePermissionUsageInfo> onGetPermissionUsages(
boolean countSystem, long numMillis);
@@ -186,6 +193,7 @@
* @see PermissionManager#getRuntimePermissionsVersion()
* @see PermissionManager#setRuntimePermissionsVersion(int)
*/
+ @WorkerThread
public abstract void onGrantOrUpgradeDefaultRuntimePermissions();
/**
@@ -196,6 +204,7 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 58b70a7..24f42d4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11124,8 +11124,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
/**
@@ -11134,8 +11132,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
/**
@@ -11144,8 +11140,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
/**
@@ -11155,8 +11149,6 @@
* 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 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
/**
@@ -11195,8 +11185,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
/**
@@ -11205,8 +11193,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
/**
@@ -11215,8 +11201,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
"captive_portal_other_fallback_urls";
@@ -11226,8 +11210,6 @@
* by "@@,@@".
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
"captive_portal_fallback_probe_specs";
@@ -11238,8 +11220,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
/**
@@ -11248,8 +11228,6 @@
*
* @hide
*/
- @SystemApi
- @TestApi
public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
/**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 0051d01..bbd44c8 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -682,14 +682,6 @@
// 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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 @@
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 7ad118e..865a656 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2814,10 +2814,14 @@
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 @@
.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/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 947ff05..deb0d2a 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -57,4 +57,6 @@
int interrogatingPid, long interrogatingTid);
void clearAccessibilityFocus();
+
+ void notifyOutsideTouch();
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index e6cff80..5930f8f 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.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 @@
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 @@
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 @@
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 @@
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 @@
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;
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 8f8e4d8..f22b6cd 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -76,6 +76,7 @@
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_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 7052ed6..4fad239 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1010,9 +1010,10 @@
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 @@
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 @@
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 d3f9196..2b471fe 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -163,7 +163,7 @@
}
// Generic idmap parameters
- const char* argv[9];
+ const char* argv[10];
int argc = 0;
struct stat st;
@@ -203,6 +203,10 @@
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 @@
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 a900294..ccadc7d 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -212,6 +212,10 @@
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 77ebd02..fa5f931 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -104,6 +104,8 @@
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 @@
|| 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/content/intent.proto b/core/proto/android/content/intent.proto
index 2de538d..014d71c 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/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 @@
// 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/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 0000000..838cb49
--- /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/resolver_icon_placeholder.xml b/core/res/res/drawable/resolver_icon_placeholder.xml
index 049cfee..7236fbe 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 4d5e262..8727f4c 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -25,6 +25,7 @@
android:id="@id/contentPanel">
<RelativeLayout
+ android:id="@+id/chooser_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
@@ -62,11 +63,10 @@
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"/>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 6aca49b..c5e72f0 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/colors.xml b/core/res/res/values/colors.xml
index ef26cd7..048f341 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 5af3c2a..112f98e 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/dimens.xml b/core/res/res/values/dimens.xml
index b81db15..d6ec414 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 ce7995a..2b0c86b 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/strings.xml b/core/res/res/values/strings.xml
index 4bd2cc75..ef834aa 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1507,6 +1507,13 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
<string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
+ <!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_recalibrate_notification_name">Face Authentication</string>
+ <!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_recalibrate_notification_title">Re-enroll your face</string>
+ <!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_recalibrate_notification_content">To improve recognition, please re-enroll your face</string>
+
<!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
<string name="face_acquired_insufficient">Couldn\u2019t capture accurate face data. Try again.</string>
<!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 186b84c..470c9ed 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" />
@@ -1747,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" />
@@ -2531,6 +2532,9 @@
<java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
<!-- Face authentication messages -->
+ <java-symbol type="string" name="face_recalibrate_notification_name" />
+ <java-symbol type="string" name="face_recalibrate_notification_title" />
+ <java-symbol type="string" name="face_recalibrate_notification_content" />
<java-symbol type="string" name="face_error_unable_to_process" />
<java-symbol type="string" name="face_error_hw_not_available" />
<java-symbol type="string" name="face_error_no_space" />
@@ -2784,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" />
@@ -3760,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/tests/coretests/res/values/overlayable_icons_test.xml b/core/tests/coretests/res/values/overlayable_icons_test.xml
new file mode 100644
index 0000000..9bc46de
--- /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/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c8b361b..6d20ec3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -596,8 +596,8 @@
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 21609d3..4755cb8 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -76,6 +76,7 @@
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 a015eab..66fba26b 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -63,6 +63,7 @@
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/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp
index 70a5f5a..da6a260 100644
--- a/libs/hwui/tests/unit/CommonPoolTests.cpp
+++ b/libs/hwui/tests/unit/CommonPoolTests.cpp
@@ -36,7 +36,9 @@
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 @@
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 @@
}
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 bbb1fe3..c7f1129 100644
--- a/libs/protoutil/src/ProtoFileReader.cpp
+++ b/libs/protoutil/src/ProtoFileReader.cpp
@@ -99,6 +99,7 @@
// Shouldn't get to here. Always call hasNext() before calling next().
return 0;
}
+ mPos++;
return mBuffer[mOffset++];
}
@@ -130,6 +131,7 @@
const size_t chunk =
mMaxOffset - mOffset > amt ? amt : mMaxOffset - mOffset;
mOffset += chunk;
+ mPos += chunk;
amt -= chunk;
}
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a9150d4..977e790 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -786,8 +786,13 @@
/**
* 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 @@
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 @@
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 79d8be1..3500475 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -414,18 +414,25 @@
/**
* 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) {
- try {
- mSessionInfo = mSessionBinder.getSessionInfo();
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in getSessionInfo.", e);
- }
+ if (mSessionInfo != null) {
+ return new Bundle(mSessionInfo);
}
- return 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) {
+ mSessionInfo = Bundle.EMPTY;
+ }
+ return new Bundle(mSessionInfo);
}
/**
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index d07052b..7ee2f0a 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -411,7 +411,15 @@
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 34fd703..49d78b6d5 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 9a074dd..728a239a 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/headsup_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
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/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 a0f2367..6e7be06 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 @@
*/
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 @@
@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 ae99126..ea29ebb 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.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 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 @@
new CloseNotificationGestureListener() {
@Override
protected void close() {
- animateCollapsePanels();
+ if (mPanelExpanded) {
+ animateCollapsePanels();
+ }
}
});
// Attached to the NavBars to close the notification shade
@@ -351,7 +359,9 @@
new NavBarCloseNotificationGestureListener() {
@Override
protected void close() {
- animateCollapsePanels();
+ if (mPanelExpanded) {
+ animateCollapsePanels();
+ }
}
});
mNavBarNotificationTouchListener =
@@ -404,11 +414,20 @@
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);
@@ -505,6 +524,13 @@
mNotificationViewController.enable();
}
+ /**
+ * @return true if the notification panel is currently visible
+ */
+ boolean isNotificationPanelOpen() {
+ return mPanelExpanded;
+ }
+
@Override
public void animateExpandNotificationsPanel() {
if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
@@ -1093,4 +1119,48 @@
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 de35441..58deb11 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 52e54f9..69e91d1 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 @@
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.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 @@
// 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 @@
@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 @@
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 c51be88..a982d52 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 @@
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 09b319e..5722e09 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.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 @@
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 @@
}
@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 704788d..478ed6b 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 -->
diff --git a/packages/NetworkStack/res/values/overlayable.xml b/packages/NetworkStack/res/values/overlayable.xml
index 7eeeefc..b9d5337 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/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 97e8186..2934e1c 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -63,6 +63,49 @@
*/
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/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 093235e..bacec78 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -44,6 +44,12 @@
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;
@@ -59,6 +65,7 @@
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;
@@ -122,6 +129,7 @@
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;
/**
@@ -136,8 +144,13 @@
+ "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);
@@ -1168,10 +1181,10 @@
}
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() {
@@ -1181,8 +1194,34 @@
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));
+ }
}
/**
@@ -1194,8 +1233,7 @@
*/
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() {
@@ -1224,8 +1262,8 @@
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)) {
@@ -1446,6 +1484,45 @@
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)) {
@@ -1457,7 +1534,7 @@
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());
@@ -1782,6 +1859,10 @@
return new OneAddressPerFamilyNetwork(network);
}
+ public DnsResolver getDnsResolver() {
+ return DnsResolver.getInstance();
+ }
+
public Random getRandom() {
return new Random();
}
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 594f2ca..0dc1cbf 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -33,6 +33,7 @@
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;
@@ -41,6 +42,7 @@
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;
@@ -55,6 +57,7 @@
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;
@@ -69,6 +72,7 @@
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;
@@ -79,6 +83,7 @@
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;
@@ -96,8 +101,12 @@
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;
@@ -111,6 +120,7 @@
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;
@@ -156,10 +166,36 @@
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);
@@ -204,9 +240,8 @@
}).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));
@@ -313,6 +348,44 @@
}
@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);
@@ -642,6 +715,45 @@
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(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a5b7849..cd97ce8 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 @@
}
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/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 463367b..86ed9e3 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 8bbc270..6e4416a 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 0000000..d9299ec
--- /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 0000000..4ea3764
--- /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 0000000..5c2857a
--- /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 7074130..de54b10 100644
--- a/packages/SystemUI/res/drawable/ic_notification_gentle.xml
+++ b/packages/SystemUI/res/drawable/ic_notification_gentle.xml
@@ -18,18 +18,18 @@
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 0a8b3b8..f49aa4a 100644
--- a/packages/SystemUI/res/drawable/ic_notification_interruptive.xml
+++ b/packages/SystemUI/res/drawable/ic_notification_interruptive.xml
@@ -19,18 +19,18 @@
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 675aee9..719b702 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 e818455..953d4fe 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 f7c6c43..a94ae0f 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_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:layout_centerVertical="true"
- android:layout_alignParentEnd="true"
-
- android:orientation="horizontal">
- <!-- Optional link to app. Only appears if the channel is not disabled and the app
+ 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="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>
+ <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: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:tint="@color/notification_guts_link_icon_tint"/>
</RelativeLayout>
<!-- Channel Info Block -->
@@ -111,40 +104,22 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 8a0aaea..f9bf47b 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 ac7e6b8..1e0600e 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 2c66454..017ca69 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 b2a5075..d2a005f 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 7d76160..df6bc20 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 96aa6ab..a2039d0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1651,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>
- <!-- 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=100] Notification Importance title -->
+ <string name="notification_alert_title">Prioritized</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: 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>
+
+ <!-- [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>
+
+ <!-- [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 eb68b0f..9b471c9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -426,57 +426,71 @@
<style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase">
</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">
- <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
- <item name="android:textSize">16sp</item>
- <item name="android:alpha">0.87</item>
- </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.Secondary">
<item name="android:textSize">14sp</item>
<item name="android:alpha">0.54</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>
-
- <style name="TextAppearance.NotificationInfo.HintText">
- <item name="android:textSize">12sp</item>
- <item name="android:alpha">0.54</item>
- </style>
-
- <style name="TextAppearance.NotificationInfo.Secondary.Warning">
- <item name="android:textColor">?android:attr/colorError</item>
- </style>
-
- <style name="TextAppearance.NotificationInfo.Secondary.Link">
- <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:fontFamily">@*android:string/config_bodyFontFamilyMedium</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:gravity">center_vertical</item>
<item name="android:focusable">true</item>
</style>
+ <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.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.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.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.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.NotificationImportanceButton.Selected" parent="TextAppearance.NotificationImportanceButton">
+ <item name="android:textColor">?android:attr/colorAccent</item>
+ </style>
+
+ <style name="TextAppearance.NotificationImportanceButton.Unselected" parent="TextAppearance.NotificationImportanceButton">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
<style name="TextAppearance.HeadsUpStatusBarText"
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index fbb30d2..20de4d1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -3,7 +3,6 @@
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.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 @@
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 @@
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 @@
mSysuiColorExtractor = colorExtractor;
mClockManager = clockManager;
mTransition = new ClockBoundsTransition();
+ mTransitionListener = new ClockBoundsTransitionListener();
}
/**
@@ -155,6 +167,7 @@
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 @@
mClockManager.addOnClockChangedListener(mClockChangedListener);
mStatusBarStateController.addCallback(mStateListener);
mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
+ mTransition.addListener(mTransitionListener);
updateColors();
}
@@ -174,6 +188,7 @@
mClockManager.removeOnClockChangedListener(mClockChangedListener);
mStatusBarStateController.removeCallback(mStateListener);
mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
+ mTransition.removeListener(mTransitionListener);
setClockPlugin(null);
}
@@ -193,6 +208,7 @@
}
if (plugin == null) {
mClockView.setVisibility(View.VISIBLE);
+ mClockViewBold.setVisibility(View.INVISIBLE);
mKeyguardStatusArea.setVisibility(View.VISIBLE);
return;
}
@@ -203,6 +219,7 @@
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 void setStyle(Style style) {
mClockView.getPaint().setStyle(style);
+ mClockViewBold.getPaint().setStyle(style);
if (mClockPlugin != null) {
mClockPlugin.setStyle(style);
}
@@ -252,6 +270,7 @@
*/
public void setTextColor(int color) {
mClockView.setTextColor(color);
+ mClockViewBold.setTextColor(color);
if (mClockPlugin != null) {
mClockPlugin.setTextColor(color);
}
@@ -259,18 +278,22 @@
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 void refresh() {
mClockView.refresh();
+ mClockViewBold.refresh();
if (mClockPlugin != null) {
mClockPlugin.onTimeTick();
}
@@ -356,8 +380,7 @@
/**
* 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 @@
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 @@
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 @@
/**
* 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 @@
.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/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 1bd8e0d..4ed28f9 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 @@
? 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/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 39e0dff..30c97d7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -408,6 +408,7 @@
public void showConfirmationButton(boolean show) {
if (show) {
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
updateState(STATE_PENDING_CONFIRMATION);
mPositiveButton.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 744f88d..ef383ad 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.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.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 @@
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 @@
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 @@
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
mSurfaceSynchronizer = synchronizer;
+
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
/**
@@ -462,6 +457,18 @@
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 a4a0fe1..d9fe47a 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 @@
/** 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 @@
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 @@
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 @@
// 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 baeedaa..a51d46c 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 @@
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 74a6b60..eb6ac79 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 @@
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;
+ }
+ /**
+ * Animates the stack either away from the newly visible IME, or back to its original position
+ * due to the IME going away.
+ */
+ public void animateForImeVisibility(boolean imeVisible) {
final float maxBubbleY = getAllowableStackPositionRegion().bottom;
- if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) {
- mPreImeY = mStackPosition.y;
+ float destinationY = 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),
/* 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.
- */
- public void updateBoundsForInvisibleImeAndAnimate() {
- mImeHeight = 0;
-
- if (mPreImeY > Float.MIN_VALUE) {
- springFirstBubbleWithStackFollowing(
- DynamicAnimation.TRANSLATION_Y,
- getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
- .setStiffness(SpringForce.STIFFNESS_LOW),
- /* startVel */ 0f,
- mPreImeY);
- mPreImeY = Float.MIN_VALUE;
+ destinationY);
}
}
@@ -538,6 +535,7 @@
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 0fe6611..a381e7b 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 @@
}
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 @@
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 505957a..e128531 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 @@
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 60dceef..84f7e89 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 @@
// 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/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index fcaf981..89aa96db 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.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.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 @@
private QSDetail.Callback mCallback;
private BrightnessController mBrightnessController;
+ private DumpController mDumpController;
protected QSTileHost mHost;
protected QSSecurityFooter mFooter;
@@ -93,6 +103,12 @@
}
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 @@
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_slider));
+ mDumpController = dumpController;
}
protected void addDivider() {
@@ -170,6 +187,7 @@
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.addCallback(this);
}
+ if (mDumpController != null) mDumpController.addListener(this);
}
@Override
@@ -184,6 +202,7 @@
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.removeCallback(this);
}
+ if (mDumpController != null) mDumpController.removeListener(this);
super.onDetachedFromWindow();
}
@@ -649,6 +668,18 @@
}
}
+ @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 dfc3e66..3c4898c 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.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.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 @@
/** 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 @@
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 @@
@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 @@
mTunerService.removeTunable(this);
mServices.destroy();
mPluginManager.removePluginListener(this);
+ mDumpController.removeListener(this);
}
@Override
@@ -363,4 +372,11 @@
}
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 4b55137..73f6fc5 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.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 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 @@
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());
}
@@ -187,6 +195,10 @@
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
@@ -200,13 +212,6 @@
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;
@@ -238,8 +243,6 @@
mCellWidth = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
mCellHeight = mCellWidth;
- updateLayoutParams();
-
return false;
}
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 3c59f69..31526bf 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 @@
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 @@
}
@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 @@
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 a732a25..c186e59 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 @@
}
}
+ @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 e275690..1f857ff 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.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.QSHost;
import com.android.systemui.qs.QuickStatusBarHeader;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -74,7 +77,7 @@
*
* @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 @@
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/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index ebafb81..c886062 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.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 @@
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 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 @@
*/
@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 @@
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 @@
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 @@
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}.
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 622b869..e6875e4 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 @@
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.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.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 @@
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 @@
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 @@
private OnClickListener mOnAlert = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
mChosenImportance = IMPORTANCE_DEFAULT;
+ setImportanceSummary(ACTION_ALERT);
updateButtons(ACTION_ALERT);
};
@@ -140,6 +140,7 @@
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 @@
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 @@
&& numTotalChannels == 1;
}
+ mShowInStatusBar = !mINotificationManager.shouldHideSilentStatusIcons(
+ mContext.getPackageName());
+ // TODO: b/128445911 use show on lockscreen setting
+
bindHeader();
bindChannelDetails();
@@ -334,6 +333,7 @@
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 @@
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 @@
}
}
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 @@
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 @@
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 @@
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/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 6121ae6..586e82c 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 @@
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 2a933a7..742fdda 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 @@
// Send the assistant availability upon connection
if (isConnected) {
- mNavigationBarView.setAssistantAvailable(mAssistantAvailable);
+ sendAssistantAvailability(mAssistantAvailable);
}
}
@@ -235,7 +235,7 @@
boolean available = mAssistManager
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
if (mAssistantAvailable != available) {
- mNavigationBarView.setAssistantAvailable(available);
+ sendAssistantAvailability(available);
mAssistantAvailable = available;
}
}
@@ -903,6 +903,17 @@
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 a45d86e..411378f 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 @@
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 @@
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 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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3f33ba6..7c271ce 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 @@
@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 @@
@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 1e486c0..d521e55 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.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 @@
* 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 8d9c5a3..43912f9 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_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 @@
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 @@
if (!mHasSeenODICaptionsTooltip && !fromDismiss && mODICaptionsTooltipViewStub != null) {
mController.getCaptionsComponentState(true);
} else {
- if (mHasSeenODICaptionsTooltip && mODICaptionsTooltipView != null) {
+ if (mHasSeenODICaptionsTooltip && fromDismiss && mODICaptionsTooltipView != null) {
hideCaptionsTooltip();
}
}
@@ -565,13 +567,14 @@
}
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 @@
}
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 @@
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 0000000..5cc7976
--- /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/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java
index da31134..bafae6c 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 android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.View;
import android.widget.TextView;
import androidx.test.filters.SmallTest;
@@ -53,22 +52,11 @@
}
@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 10d1572..db4f5ff 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 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 @@
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 c6b31d0..38cdee4 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 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.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 @@
@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 @@
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 @@
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 a458928..f73472f 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 @@
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 03278b4..183e027 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.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 @@
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 8a36cfb..63ebe92 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 @@
public class QSIconViewImplTest extends SysuiTestCase {
private QSIconViewImpl mIconView;
+ private static int RES_ID = 1;
@Before
public void setup() {
@@ -81,4 +84,24 @@
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 3df8a8a..192d8f8 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.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.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 80c51cf..375b6e54 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 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.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 @@
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 @@
Looper.prepare();
}
mController = new KeyguardIndicationController(mContext, mIndicationArea, mLockIcon,
- mLockPatternUtils, mWakeLock);
+ mLockPatternUtils, mWakeLock, mShadeController, mAccessibilityController);
}
@Test
@@ -222,4 +231,23 @@
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 d2f8e02..02731c0 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@Test
public void testCloseControls_blockingHelperDismissedIfShown() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
@@ -629,7 +629,7 @@
@Test
public void testSilentlyChangedCallsUpdateNotificationChannel_blockingHelper()
throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
@@ -644,7 +644,7 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
IMPORTANCE_DEFAULT,
- false);
+ true);
mNotificationInfo.findViewById(R.id.deliver_silently).performClick();
waitForUndoButton();
@@ -684,7 +684,7 @@
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 @@
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 @@
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 @@
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 @@
}
@Test
- public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
+ public void testSilenceCallsUpdateNotificationChannel_channelImportanceMin()
throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+ 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);
- mNotificationInfo.findViewById(R.id.alert_row).performClick();
+ 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_LOW);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_LOW, false);
+
+ mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
mNotificationInfo.handleCloseControls(true, false);
@@ -790,7 +858,7 @@
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 @@
}, 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/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 334262f..05b937a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2722,9 +2722,7 @@
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 @@
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 @@
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 @@
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/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index ce1d33a..6c1ffa7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3999,8 +3999,6 @@
@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/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 99380c9..19c3a71 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 @@
}
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/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index e218c6b..44fd846 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -23,7 +23,12 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -48,10 +53,10 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
@@ -124,6 +129,49 @@
// but let's leave it as-is for now.
return result || !authenticated;
}
+
+ @Override
+ public boolean onAcquired(int acquireInfo, int vendorCode) {
+
+ if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
+ final String name =
+ getContext().getString(R.string.face_recalibrate_notification_name);
+ final String title =
+ getContext().getString(R.string.face_recalibrate_notification_title);
+ final String content =
+ getContext().getString(R.string.face_recalibrate_notification_content);
+
+ final Intent intent = new Intent("android.settings.FACE_SETTINGS");
+ intent.setPackage("com.android.settings");
+
+ final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(),
+ 0 /* requestCode */, intent, 0 /* flags */, null /* options */,
+ UserHandle.CURRENT);
+
+ final String id = "FaceService";
+
+ NotificationManager nm =
+ getContext().getSystemService(NotificationManager.class);
+ NotificationChannel channel = new NotificationChannel(id, name,
+ NotificationManager.IMPORTANCE_HIGH);
+ Notification notification = new Notification.Builder(getContext(), id)
+ .setSmallIcon(R.drawable.ic_lock)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setSubText(name)
+ .setOnlyAlertOnce(true)
+ .setLocalOnly(true)
+ .setAutoCancel(true)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setContentIntent(pendingIntent)
+ .build();
+
+ nm.createNotificationChannel(channel);
+ nm.notifyAsUser(null /* tag */, 0 /* id */, notification, UserHandle.CURRENT);
+ }
+
+ return super.onAcquired(acquireInfo, vendorCode);
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 1913635..e33392d 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.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 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 @@
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/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index 60be70e..8391f9d 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -288,6 +288,18 @@
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) {
@@ -376,20 +388,37 @@
private void handleNfwNotification(NfwNotification nfwNotification) {
if (DEBUG) Log.d(TAG, "Non-framework location access notification: " + nfwNotification);
- final String proxyAppPackageName = nfwNotification.mProxyAppPackageName;
- 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);
+ if (nfwNotification.isEmergencyRequestNotification()) {
+ handleEmergencyNfwNotification(nfwNotification);
+ return;
}
+
+ final String proxyAppPackageName = nfwNotification.mProxyAppPackageName;
+ final Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get(
+ proxyAppPackageName);
+ 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;
@@ -423,6 +452,16 @@
}
}
+ 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 174f6009..69efd02 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -24,7 +24,10 @@
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;
@@ -65,6 +68,8 @@
private boolean mUseBpfStats;
+ private INetd mNetdService;
+
// A persistent Snapshot since device start for eBPF stats
@GuardedBy("mPersistSnapshot")
private final NetworkStats mPersistSnapshot;
@@ -274,6 +279,19 @@
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 {
@@ -287,6 +305,13 @@
}
if (mUseBpfStats) {
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");
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index b85abd9..61be1f5 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 @@
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 e5ecd49..9fc30eb 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 @@
}
}
}
+
+ @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
@@ -4782,6 +4800,19 @@
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
@@ -4807,12 +4838,7 @@
// 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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3c336ea..a25f8c0 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 @@
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 {
@@ -2631,6 +2633,13 @@
| 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/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 654c477..0e20905 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.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 @@
// 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 9cbf00b..af009ec 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 @@
}
@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/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 9f7cb3d3..6318486 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 @@
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 a53f85d..155ab41 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_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 @@
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/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a57dd6d..43d9e43 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 @@
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 @@
}
};
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- private NavigationBarExperiments mExperiments = new NavigationBarExperiments();
- // EXPERIMENT END
-
@GuardedBy("mHandler")
private SleepToken mDreamingSleepToken;
@@ -1621,11 +1620,9 @@
// 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);
@@ -1648,11 +1645,7 @@
// 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);
@@ -1675,11 +1668,7 @@
// 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);
@@ -1887,10 +1876,21 @@
}
}
- // 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;
@@ -2623,6 +2623,7 @@
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] =
@@ -2646,6 +2647,14 @@
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] =
@@ -2674,16 +2683,10 @@
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());
@@ -2749,6 +2752,26 @@
}
/**
+ * 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 bb3ff5e..0000000
--- 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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4105487..c29b132 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 @@
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 f0b9c62..f65f0ab 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 @@
mOwnerCanManageAppTokens);
return mOwnerCanManageAppTokens && (layer > navLayer);
}
+
+ float getWindowCornerRadiusForAnimation() {
+ return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0;
+ }
}
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 3f9a57e..3cdadd5 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 @@
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 @@
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/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2d101dd..34bb0a8 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.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 @@
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/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d4f9874..32105ad 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2064,7 +2064,6 @@
} else {
logd("putPhoneIdAndSubIdExtra: no valid subs");
intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
- intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
}
}
@@ -2075,9 +2074,6 @@
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/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index d41a5d6..f9304f2 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 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 @@
}
}
+ @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/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 44380cc..5f08a34 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.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 @@
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index fd59390a..70ca088 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -129,6 +129,8 @@
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);