Merge "Race condition while injecting ACTION_OUTSIDE" into qt-dev
diff --git a/Android.mk b/Android.mk
index c58f7af..9bda2dc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,27 +32,6 @@
# ============================================================
include $(CLEAR_VARS)
-aidl_parcelables :=
-define stubs-to-aidl-parcelables
- gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl
- aidl_parcelables += $$(gen)
- $$(gen): $(call java-lib-header-files,$1) $(HOST_OUT_EXECUTABLES)/sdkparcelables
- @echo Extract SDK parcelables: $$@
- rm -f $$@
- $(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@
-endef
-
-$(foreach stubs,android_stubs_current android_test_stubs_current android_system_stubs_current,\
- $(eval $(call stubs-to-aidl-parcelables,$(stubs))))
-
-gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
-.KATI_RESTAT: $(gen)
-$(gen): $(aidl_parcelables)
- @echo Combining SDK parcelables: $@
- rm -f $@.tmp
- cat $^ | sort -u > $@.tmp
- $(call commit-change-for-toc,$@)
-
# This is used by ide.mk as the list of source files that are
# always included.
INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index 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/current.txt b/api/current.txt
index 44b11e1..0f4231d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1223,8 +1223,8 @@
field public static final int shadowRadius = 16843108; // 0x1010164
field public static final int shape = 16843162; // 0x101019a
field public static final int shareInterpolator = 16843195; // 0x10101bb
- field public static final int sharedUserId = 16842763; // 0x101000b
- field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b
+ field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
field public static final int shell = 16844180; // 0x1010594
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
field public static final int shortcutId = 16844072; // 0x1010528
@@ -27245,7 +27245,7 @@
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);
diff --git a/api/system-current.txt b/api/system-current.txt
index d08039d..288cb9c 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
@@ -3079,16 +3078,15 @@
}
public final class GnssCapabilities {
- method public boolean hasCapability(int);
- field public static final int GEOFENCING = 2; // 0x2
- field public static final int LOW_POWER_MODE = 0; // 0x0
- field public static final int MEASUREMENTS = 3; // 0x3
- field public static final int MEASUREMENT_CORRECTIONS = 5; // 0x5
- field public static final int MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 7; // 0x7
- field public static final int MEASUREMENT_CORRECTIONS_LOS_SATS = 6; // 0x6
- field public static final int MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 8; // 0x8
- field public static final int NAV_MESSAGES = 4; // 0x4
- field public static final int SATELLITE_BLACKLIST = 1; // 0x1
+ method public boolean hasGeofencing();
+ method public boolean hasLowPowerMode();
+ method public boolean hasMeasurementCorrections();
+ method public boolean hasMeasurementCorrectionsExcessPathLength();
+ method public boolean hasMeasurementCorrectionsLosSats();
+ method public boolean hasMeasurementCorrectionsReflectingPane();
+ method public boolean hasMeasurements();
+ method public boolean hasNavMessages();
+ method public boolean hasSatelliteBlacklist();
}
public final class GnssMeasurementCorrections implements android.os.Parcelable {
@@ -3399,7 +3397,7 @@
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
method @Nullable public String getExtraLocationControllerPackage();
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize();
- method @Nullable public android.location.GnssCapabilities getGnssCapabilities();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.location.GnssCapabilities getGnssCapabilities();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
method public boolean isExtraLocationControllerPackageEnabled();
method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
@@ -9479,7 +9477,7 @@
package android.view.accessibility {
public final class AccessibilityManager {
- method public int getAccessibilityWindowId(android.os.IBinder);
+ method public int getAccessibilityWindowId(@Nullable android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
}
diff --git a/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/cmds/incidentd/tests/ProtoFileReader_test.cpp b/cmds/incidentd/tests/ProtoFileReader_test.cpp
new file mode 100644
index 0000000..acb1bbb
--- /dev/null
+++ b/cmds/incidentd/tests/ProtoFileReader_test.cpp
@@ -0,0 +1,100 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#define DEBUG false
+#include "Log.h"
+
+#include <android-base/file.h>
+#include <android/util/ProtoFileReader.h>
+#include <android/util/ProtoOutputStream.h>
+#include <android/util/protobuf.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <signal.h>
+#include <string.h>
+
+#include "FdBuffer.h"
+#include "incidentd_util.h"
+
+using namespace android;
+using namespace android::base;
+using namespace android::os::incidentd;
+using ::testing::Test;
+
+const std::string kTestPath = GetExecutableDirectory();
+const std::string kTestDataPath = kTestPath + "/testdata/";
+
+status_t read(sp<ProtoFileReader> reader, size_t size) {
+ uint8_t const* buf;
+ while (size > 0 && (buf = reader->readBuffer()) != nullptr) {
+ size_t amt = reader->currentToRead();
+ if (size < amt) {
+ amt = size;
+ }
+ reader->move(amt);
+ size -= amt;
+ }
+
+ return NO_ERROR;
+}
+
+TEST(ProtoFileReaderTest, ParseOneLevel) {
+ const std::string testFile = kTestDataPath + "protoFile.txt";
+ size_t msg1Size = 10;
+ size_t msg2Size = 5 * 1024;
+ {
+ // Create a proto file
+ // TestProto {
+ // optional Section1 section1 = 1;
+ // optional Section2 section2 = 2;
+ // }
+
+ unique_fd fd(open(testFile.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
+ ASSERT_NE(fd.get(), -1);
+ ProtoOutputStream proto;
+ string field1;
+ field1.resize(msg1Size, 'h');
+ string field2;
+ field2.resize(msg2Size, 'a');
+ proto.write(FIELD_TYPE_MESSAGE | 1, field1.data(), field1.length());
+ proto.write(FIELD_TYPE_MESSAGE | 2, field2.data(), field2.length());
+ proto.flush(fd);
+ }
+
+ int fd = open(testFile.c_str(), O_RDONLY | O_CLOEXEC);
+ ASSERT_NE(fd, -1);
+
+ status_t err;
+ sp<ProtoFileReader> reader = new ProtoFileReader(fd);
+ int i = 0;
+ size_t msg_size[2];
+ while (reader->hasNext()) {
+ uint64_t fieldTag = reader->readRawVarint();
+ uint32_t fieldId = read_field_id(fieldTag);
+ uint8_t wireType = read_wire_type(fieldTag);
+ ASSERT_EQ(WIRE_TYPE_LENGTH_DELIMITED, wireType);
+ size_t sectionSize = reader->readRawVarint();
+ if (i < 2) {
+ msg_size[i] = sectionSize;
+ }
+ err = read(reader, sectionSize);
+ ASSERT_EQ(NO_ERROR, err);
+ i++;
+ }
+
+ ASSERT_EQ(2, i);
+
+ ASSERT_EQ(msg1Size, msg_size[0]);
+ ASSERT_EQ(msg2Size, msg_size[1]);
+ close(fd);
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0d22f3a..c8bd275 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -127,6 +127,7 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -723,7 +724,7 @@
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
- AutofillManager.AutofillClient {
+ AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
@@ -1125,6 +1126,12 @@
return this;
}
+ /** @hide */
+ @Override
+ public final ContentCaptureClient getContentCaptureClient() {
+ return this;
+ }
+
/**
* Register an {@link Application.ActivityLifecycleCallbacks} instance that receives
* lifecycle callbacks for only this Activity.
@@ -6511,6 +6518,12 @@
return getComponentName();
}
+ /** @hide */
+ @Override
+ public final ComponentName contentCaptureClientGetComponentName() {
+ return getComponentName();
+ }
+
/**
* Retrieve a {@link SharedPreferences} object for accessing preferences
* that are private to this activity. This simply calls the underlying
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 13add09..9cd42a5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5719,14 +5719,18 @@
if (packages == null) {
break;
}
+
+ List<String> packagesHandled = new ArrayList<>();
+
synchronized (mResourcesManager) {
for (int i = packages.length - 1; i >= 0; i--) {
- WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
+ String packageName = packages[i];
+ WeakReference<LoadedApk> ref = mPackages.get(packageName);
LoadedApk pkgInfo = ref != null ? ref.get() : null;
if (pkgInfo != null) {
hasPkgInfo = true;
} else {
- ref = mResourcePackages.get(packages[i]);
+ ref = mResourcePackages.get(packageName);
pkgInfo = ref != null ? ref.get() : null;
if (pkgInfo != null) {
hasPkgInfo = true;
@@ -5737,8 +5741,8 @@
// Adjust it's internal references to the application info and
// resources.
if (pkgInfo != null) {
+ packagesHandled.add(packageName);
try {
- final String packageName = packages[i];
final ApplicationInfo aInfo =
sPackageManager.getApplicationInfo(
packageName,
@@ -5770,6 +5774,13 @@
}
}
}
+
+ try {
+ getPackageManager().notifyPackagesReplacedReceived(
+ packagesHandled.toArray(new String[0]));
+ } catch (RemoteException ignored) {
+ }
+
break;
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b93aaa2..40cb29f 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -236,7 +236,7 @@
config.screenLayout = Configuration.reduceScreenLayout(sl,
config.screenHeightDp, config.screenWidthDp);
}
- config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
+ config.smallestScreenWidthDp = Math.min(config.screenWidthDp, config.screenHeightDp);
config.compatScreenWidthDp = config.screenWidthDp;
config.compatScreenHeightDp = config.screenHeightDp;
config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 98b658d..c48c878 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1143,7 +1143,7 @@
Context outerContext = ctx.getOuterContext();
ContentCaptureOptions options = outerContext.getContentCaptureOptions();
// Options is null when the service didn't whitelist the activity or package
- if (options != null) {
+ if (options != null && (options.lite || options.isWhitelisted(outerContext))) {
IBinder b = ServiceManager
.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index 76c4fb8..cb2142c 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -24,6 +24,9 @@
import android.util.ArraySet;
import android.util.Log;
import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
@@ -78,12 +81,19 @@
*/
public final boolean lite;
+ /**
+ * Constructor for "lite" objects that are just used to enable a {@link ContentCaptureManager}
+ * for contexts belonging to the content capture service app.
+ */
public ContentCaptureOptions(int loggingLevel) {
this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0,
/* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0,
/* logHistorySize= */ 0, /* whitelistedComponents= */ null);
}
+ /**
+ * Default constructor.
+ */
public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
int textChangeFlushingFrequencyMs, int logHistorySize,
@Nullable ArraySet<ComponentName> whitelistedComponents) {
@@ -91,6 +101,16 @@
textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
}
+ /** @hide */
+ @VisibleForTesting
+ public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) {
+ this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE,
+ ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE,
+ ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS,
+ ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS,
+ ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents);
+ }
+
private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize,
int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize,
@Nullable ArraySet<ComponentName> whitelistedComponents) {
@@ -103,10 +123,6 @@
this.whitelistedComponents = whitelistedComponents;
}
- /**
- * @hide
- */
- @TestApi
public static ContentCaptureOptions forWhitelistingItself() {
final ActivityThread at = ActivityThread.currentActivityThread();
if (at == null) {
@@ -120,19 +136,27 @@
throw new SecurityException("Thou shall not pass!");
}
- final ContentCaptureOptions options = new ContentCaptureOptions(
- ContentCaptureManager.LOGGING_LEVEL_VERBOSE,
- ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE,
- ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS,
- ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS,
- ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE,
- /* whitelistedComponents= */ null);
+ final ContentCaptureOptions options =
+ new ContentCaptureOptions(/* whitelistedComponents= */ null);
// Always log, as it's used by test only
Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options);
return options;
}
+ /** @hide */
+ @VisibleForTesting
+ public boolean isWhitelisted(@NonNull Context context) {
+ if (whitelistedComponents == null) return true; // whole package is whitelisted
+ final ContentCaptureClient client = context.getContentCaptureClient();
+ if (client == null) {
+ // Shouldn't happen, but it doesn't hurt to check...
+ Log.w(TAG, "isWhitelisted(): no ContentCaptureClient on " + context);
+ return false;
+ }
+ return whitelistedComponents.contains(client.contentCaptureClientGetComponentName());
+ }
+
@Override
public String toString() {
if (lite) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index de04829..00238bf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -70,6 +70,7 @@
import android.view.ViewDebug;
import android.view.WindowManager;
import android.view.autofill.AutofillManager.AutofillClient;
+import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.view.textclassifier.TextClassificationManager;
import java.io.File;
@@ -5414,6 +5415,14 @@
/**
* @hide
*/
+ @Nullable
+ public ContentCaptureClient getContentCaptureClient() {
+ return null;
+ }
+
+ /**
+ * @hide
+ */
public final boolean isAutofillCompatibilityEnabled() {
final AutofillOptions options = getAutofillOptions();
return options != null && options.compatModeEnabled;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 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/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index cf704d5..6ab4657 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -770,4 +770,6 @@
int getRuntimePermissionsVersion(int userId);
void setRuntimePermissionsVersion(int version, int userId);
+
+ void notifyPackagesReplacedReceived(in String[] packages);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 83e15e8..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;
/**
@@ -2030,9 +2030,10 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API
- * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate
- * what level of optional hardware features limits it supports.
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan
+ * implementation on this device is hardware accelerated, and the Vulkan native API will
+ * enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate what
+ * level of optional hardware features limits it supports.
* <p>
* Level 0 includes the base Vulkan requirements as well as:
* <ul><li>{@code VkPhysicalDeviceFeatures::textureCompressionETC2}</li></ul>
@@ -2057,10 +2058,10 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API
- * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate
- * what level of optional compute features that device supports beyond the Vulkan 1.0
- * requirements.
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan
+ * implementation on this device is hardware accelerated, and the Vulkan native API will
+ * enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate what
+ * level of optional compute features that device supports beyond the Vulkan 1.0 requirements.
* <p>
* Compute level 0 indicates:
* <ul>
@@ -2075,10 +2076,11 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature(String, int)}: The version of this feature indicates the highest
- * {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices that support
- * the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The feature version
- * uses the same encoding as Vulkan version numbers:
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan
+ * implementation on this device is hardware accelerated, and the feature version will indicate
+ * the highest {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices
+ * that support the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The
+ * feature version uses the same encoding as Vulkan version numbers:
* <ul>
* <li>Major version number in bits 31-22</li>
* <li>Minor version number in bits 21-12</li>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2906710..0e10de8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -510,7 +510,7 @@
* The absence of a connection type.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public static final int TYPE_NONE = -1;
/**
@@ -627,7 +627,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public static final int TYPE_MOBILE_FOTA = 10;
/**
@@ -645,7 +645,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public static final int TYPE_MOBILE_CBS = 12;
/**
@@ -655,7 +655,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public static final int TYPE_WIFI_P2P = 13;
/**
@@ -674,7 +674,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public static final int TYPE_MOBILE_EMERGENCY = 15;
/**
@@ -775,7 +775,7 @@
*/
public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
/**
* A kludge to facilitate static access where a Context pointer isn't available, like in the
@@ -867,7 +867,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public static boolean isNetworkTypeMobile(int networkType) {
switch (networkType) {
case TYPE_MOBILE:
@@ -1304,7 +1304,7 @@
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public LinkProperties getLinkProperties(int networkType) {
try {
return mService.getLinkPropertiesForType(networkType);
@@ -3042,7 +3042,7 @@
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
public boolean isNetworkSupported(int networkType) {
try {
return mService.isNetworkSupported(networkType);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 4332d8a..1c6a484 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -25,6 +25,7 @@
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.media.MediaPlayer;
+import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.DataUnit;
@@ -169,7 +170,7 @@
private static INetworkStatsService sStatsService;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private synchronized static INetworkStatsService getStatsService() {
if (sStatsService == null) {
sStatsService = INetworkStatsService.Stub.asInterface(
@@ -979,7 +980,7 @@
* Interfaces are never removed from this list, so counters should always be
* monotonic.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private static String[] getMobileIfaces() {
try {
return getStatsService().getMobileIfaces();
diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index 5b67406..cf08b65 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -22,6 +22,8 @@
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
*
@@ -837,15 +839,11 @@
* @param string the escaped string
* @return the unescaped string.
*/
+ private static final Pattern plusOrPercent = Pattern.compile("[+%]");
public String unescape(String string) {
- // Early exit if no escaped characters.
- int firstEscape = string.indexOf('%');
- if ( firstEscape < 0) {
- firstEscape = string.indexOf('+');
- if (firstEscape < 0) {
- return string;
- }
- }
+ final Matcher matcher = plusOrPercent.matcher(string);
+ if (!matcher.find()) return string;
+ final int firstEscape = matcher.start();
int length = string.length();
@@ -855,8 +853,7 @@
char c = string.charAt(i);
if (c == '+') {
c = ' ';
- }
- else if ( c == '%' && i + 2 < length) {
+ } else if (c == '%' && i + 2 < length) {
char c1 = string.charAt(i + 1);
char c2 = string.charAt(i + 2);
if (isHexDigit(c1) && isHexDigit(c2)) {
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index a51a871..232869d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -453,14 +453,22 @@
final boolean appIsProfileable = isProfileable(context);
final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1;
if (appIsDebuggable || appIsProfileable || deviceIsDebuggable) {
+ String debugPackage;
- String debugPackage =
- coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ if (coreSettings != null) {
+ debugPackage =
+ coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ } else {
+ ContentResolver contentResolver = context.getContentResolver();
+ debugPackage = Settings.Global.getString(contentResolver,
+ Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+ }
if ((debugPackage != null) && (!debugPackage.isEmpty())) {
return debugPackage;
}
}
+
return "";
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 7d61bf6..2fff595 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -555,6 +555,18 @@
}
/**
+ * @hide
+ */
+ public static class WakeData {
+ public WakeData(long wakeTime, @WakeReason int wakeReason) {
+ this.wakeTime = wakeTime;
+ this.wakeReason = wakeReason;
+ }
+ public long wakeTime;
+ public @WakeReason int wakeReason;
+ }
+
+ /**
* The value to pass as the 'reason' argument to reboot() to reboot into
* recovery mode for tasks other than applying system updates, such as
* doing factory resets.
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index d55489a..9661a08 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -203,4 +203,7 @@
/** Returns whether there hasn't been a user activity event for the given number of ms. */
public abstract boolean wasDeviceIdleFor(long ms);
+
+ /** Returns information about the last wakeup event. */
+ public abstract PowerManager.WakeData getLastWakeup();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ac59101..58b70a7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14712,6 +14712,18 @@
public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS =
"text_classifier_action_model_params";
+ /**
+ * The amount of time to suppress "power-off" from the power button after the device has
+ * woken due to a gesture (lifting the phone). Since users have learned to hit the power
+ * button immediately when lifting their device, it can cause the device to turn off if a
+ * gesture has just woken the device. This value tells us the milliseconds to wait after
+ * a gesture before "power-off" via power-button is functional again. A value of 0 is no
+ * delay, and reverts to the old behavior.
+ *
+ * @hide
+ */
+ public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
+ "power_button_suppression_delay_after_gesture_wake";
}
/**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index f2aaead..e8b0d92 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -53,7 +53,6 @@
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
DEFAULT_FLAGS.put("settings_network_and_internet_v2", "true");
- DEFAULT_FLAGS.put("settings_slice_injection", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index 680e85f..c1873d7 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -44,6 +44,13 @@
}
/**
+ * Removes all mappings from this SparseSetArray.
+ */
+ public void clear() {
+ mData.clear();
+ }
+
+ /**
* @return whether a value exists at index n.
*/
public boolean contains(int n, T value) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index d12777f..882e6fd 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1015,7 +1015,7 @@
* @hide
*/
@SystemApi
- public int getAccessibilityWindowId(IBinder windowToken) {
+ public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
if (windowToken == null) {
return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index a977073..c2ad82f 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -294,6 +294,15 @@
private MainContentCaptureSession mMainSession;
/** @hide */
+ public interface ContentCaptureClient {
+ /**
+ * Gets the component name of the client.
+ */
+ @NonNull
+ ComponentName contentCaptureClientGetComponentName();
+ }
+
+ /** @hide */
public ContentCaptureManager(@NonNull Context context,
@NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) {
mContext = Preconditions.checkNotNull(context, "context cannot be null");
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index be66de2..ca2bc7f 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) {
@@ -1680,7 +1703,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;
}
}
@@ -1802,7 +1828,8 @@
if (info == null) return null;
// Now fetch app icon and raster with no badging even in work profile
- Bitmap appIcon = makePresentationGetter(info).getIconBitmap();
+ Bitmap appIcon = makePresentationGetter(info).getIconBitmap(
+ UserHandle.getUserHandleForUid(UserHandle.myUserId()));
// Raster target drawable with appIcon as a badge
SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 2849f57..5e4918d 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -551,11 +551,11 @@
mAi.packageName);
}
- public Drawable getIcon() {
- return new BitmapDrawable(mCtx.getResources(), getIconBitmap());
+ public Drawable getIcon(UserHandle userHandle) {
+ return new BitmapDrawable(mCtx.getResources(), getIconBitmap(userHandle));
}
- public Bitmap getIconBitmap() {
+ public Bitmap getIconBitmap(UserHandle userHandle) {
Drawable dr = null;
if (mHasSubstitutePermission) {
dr = getIconSubstituteInternal();
@@ -576,7 +576,7 @@
}
SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx);
- Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle());
+ Bitmap icon = sif.createUserBadgedIconBitmap(dr, userHandle);
sif.recycle();
return icon;
@@ -699,7 +699,8 @@
}
Drawable loadIconForResolveInfo(ResolveInfo ri) {
- return makePresentationGetter(ri).getIcon();
+ // Load icons based on the current process. If in work profile icons should be badged.
+ return makePresentationGetter(ri).getIcon(Process.myUserHandle());
}
@Override
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index 2484109..7a4e76f 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -43,7 +43,6 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
-import android.os.Process;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Pools.SynchronizedPool;
@@ -161,6 +160,7 @@
/**
* Creates bitmap using the source drawable and various parameters.
* The bitmap is visually normalized with other icons and has enough spacing to add shadow.
+ * Note: this method has been modified from iconloaderlib to remove a profile diff check.
*
* @param icon source of the icon associated with a user that has no badge,
* likely user 0
@@ -186,7 +186,7 @@
}
final Bitmap result;
- if (user != null && !Process.myUserHandle().equals(user)) {
+ if (user != null /* if modification from iconloaderlib */) {
BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
Drawable badged = mPm.getUserBadgedIcon(drawable, user);
if (badged instanceof BitmapDrawable) {
diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java
index dfa59b7..a0b2f94 100644
--- a/core/java/com/android/internal/infra/GlobalWhitelistState.java
+++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java
@@ -35,11 +35,13 @@
*
* <p>This class is thread safe.
*/
+// TODO: add unit tests
public class GlobalWhitelistState {
// Uses full-name to avoid collision with service-provided mLock
protected final Object mGlobalWhitelistStateLock = new Object();
+ // TODO: should not be exposed directly
@Nullable
@GuardedBy("mGlobalWhitelistStateLock")
protected SparseArray<WhitelistHelper> mWhitelisterHelpers;
diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java
index d7753db..9d653ba 100644
--- a/core/java/com/android/internal/infra/WhitelistHelper.java
+++ b/core/java/com/android/internal/infra/WhitelistHelper.java
@@ -98,9 +98,9 @@
@Nullable List<ComponentName> components) {
final ArraySet<String> packageNamesSet = packageNames == null ? null
: new ArraySet<>(packageNames);
- final ArraySet<ComponentName> componentssSet = components == null ? null
+ final ArraySet<ComponentName> componentsSet = components == null ? null
: new ArraySet<>(components);
- setWhitelist(packageNamesSet, componentssSet);
+ setWhitelist(packageNamesSet, componentsSet);
}
/**
@@ -170,7 +170,7 @@
pw.print("["); pw.print(components.valueAt(0));
for (int j = 1; j < components.size(); j++) {
- pw.print(", "); pw.print(components.valueAt(i));
+ pw.print(", "); pw.print(components.valueAt(j));
}
pw.println("]");
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1fc7635..72f6c12 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -609,9 +609,7 @@
int UPDATE_RADIO = 0x04;
int UPDATE_BT = 0x08;
int UPDATE_RPM = 0x10; // 16
- int UPDATE_RAIL = 0x20; // 32
- int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM
- | UPDATE_RAIL;
+ int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM;
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index 6179918..dcf8d28 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -19,10 +19,10 @@
import static android.os.Process.*;
import android.annotation.UnsupportedAppUsage;
-import android.os.FileUtils;
import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Slog;
@@ -247,6 +247,7 @@
pid = _pid;
if (parentPid < 0) {
final File procDir = new File("/proc", Integer.toString(pid));
+ uid = getUid(procDir.toString());
statFile = new File(procDir, "stat").toString();
cmdlineFile = new File(procDir, "cmdline").toString();
threadsDir = (new File(procDir, "task")).toString();
@@ -262,13 +263,22 @@
parentPid));
final File taskDir = new File(
new File(procDir, "task"), Integer.toString(pid));
+ uid = getUid(taskDir.toString());
statFile = new File(taskDir, "stat").toString();
cmdlineFile = null;
threadsDir = null;
threadStats = null;
workingThreads = null;
}
- uid = FileUtils.getUid(statFile.toString());
+ }
+
+ private static int getUid(String path) {
+ try {
+ return Os.stat(path).st_uid;
+ } catch (ErrnoException e) {
+ Slog.w(TAG, "Failed to stat(" + path + "): " + e);
+ return -1;
+ }
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 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_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 61d5031..b499981 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -256,6 +256,10 @@
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
+ if (heap == NULL) {
+ ALOGV("copyAndPost: skipping null memory callback!");
+ return;
+ }
ALOGV("copyAndPost: off=%zd, size=%zu", offset, size);
uint8_t *heapBase = (uint8_t*)heap->base();
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 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_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/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ace88f5..1ff7418 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -665,6 +665,15 @@
//ACTION: Log result for each card's eligibility check
ACTION_CONTEXTUAL_CARD_ELIGIBILITY = 1686;
+
+ // ACTION: Share a Wi-Fi network by generating a QR code
+ ACTION_SETTINGS_SHARE_WIFI_QR_CODE = 1710;
+
+ // ACTION: Connect to a Wi-Fi network by scanning a QR code
+ ACTION_SETTINGS_ENROLL_WIFI_QR_CODE = 1711;
+
+ // ACTION: Share Wi-Fi hotspot by generating a QR code
+ ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712;
}
/**
@@ -2336,4 +2345,13 @@
// CATEGORY: SETTINGS
// OS: Q
ACTION_DISPLAY_WHITE_BALANCE_SETTING_CHANGED = 1703;
+
+ // OPEN: Settings > Pick SIM dialog
+ DIALOG_SIM_LIST = 1707;
+
+ // OPEN: Settings > Pick SIM (that supports calling) dialog
+ DIALOG_CALL_SIM_LIST = 1708;
+
+ // OPEN: Settings > Pick preferred SIM dialog
+ DIALOG_PREFERRED_SIM_PICKER = 1709;
}
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index 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/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 1e0b0d8..dc2e6d5 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -478,6 +478,54 @@
}
repeated TrackedJob tracked_jobs = 4;
+ message ExecutionStats {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional JobStatusDumpProto.Bucket standby_bucket = 1;
+
+ // The time after which this record should be considered invalid (out of date), in the
+ // elapsed realtime timebase.
+ optional int64 expiration_time_elapsed = 2;
+ optional int64 window_size_ms = 3;
+
+ /** The total amount of time the app ran in its respective bucket window size. */
+ optional int64 execution_time_in_window_ms = 4;
+ optional int32 bg_job_count_in_window = 5;
+
+ /**
+ * The total amount of time the app ran in the last
+ * {@link QuotaController#MAX_PERIOD_MS}.
+ */
+ optional int64 execution_time_in_max_period_ms = 6;
+ optional int32 bg_job_count_in_max_period = 7;
+
+ /**
+ * The time after which the sum of all the app's sessions plus
+ * ConstantsProto.QuotaController.in_quota_buffer_ms equals the quota. This is only
+ * valid if
+ * execution_time_in_window_ms >=
+ * ConstantsProto.QuotaController.allowed_time_per_period_ms
+ * or
+ * execution_time_in_max_period_ms >=
+ * ConstantsProto.QuotaController.max_execution_time_ms.
+ */
+ optional int64 quota_cutoff_time_elapsed = 8;
+
+ /**
+ * The time after which job_count_in_allowed_time should be considered invalid, in the
+ * elapsed realtime timebase.
+ */
+ optional int64 job_count_expiration_time_elapsed = 9;
+
+ /**
+ * The number of jobs that ran in at least the last
+ * ConstantsProto.QuotaController.allowed_time_per_period_ms.
+ * It may contain a few stale entries since cleanup won't happen exactly every
+ * ConstantsProto.QuotaController.allowed_time_per_period_ms.
+ */
+ optional int32 job_count_in_allowed_time = 10;
+ }
+
message Package {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -517,6 +565,8 @@
optional Timer timer = 2;
repeated TimingSession saved_sessions = 3;
+
+ repeated ExecutionStats execution_stats = 4;
}
repeated PackageStats package_stats = 5;
}
diff --git a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml
new file mode 100644
index 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/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml
index 940da94..ee58738 100644
--- a/core/res/res/drawable/ic_content_copy_gm2.xml
+++ b/core/res/res/drawable/ic_content_copy_gm2.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:fillColor="#F999"
+ android:fillColor="?android:attr/textColorSecondary"
android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
</vector>
diff --git a/core/res/res/drawable/resolver_icon_placeholder.xml b/core/res/res/drawable/resolver_icon_placeholder.xml
index 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 138e24e3..8727f4c 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -25,9 +25,11 @@
android:id="@id/contentPanel">
<RelativeLayout
+ android:id="@+id/chooser_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
+ android:elevation="1dp"
android:background="@drawable/bottomsheet_background">
<ImageView
@@ -61,11 +63,11 @@
android:layout_height="match_parent"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
+ android:scrollbars="none"
android:listSelector="@color/transparent"
android:divider="@null"
- android:scrollIndicators="top"
+ android:elevation="1dp"
android:nestedScrollingEnabled="true"/>
<TextView android:id="@+id/empty"
@@ -76,6 +78,7 @@
android:text="@string/noApplications"
android:padding="@dimen/chooser_edge_margin_normal"
android:gravity="center"
+ android:elevation="1dp"
android:visibility="gone"/>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index bbc784a..26a9f57 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -346,13 +346,21 @@
By setting this value on two or more packages, each of these packages
will be given a single shared user ID, so they can for example run
in the same process. Note that for them to actually get the same
- user ID, they must also be signed with the same signature. -->
+ user ID, they must also be signed with the same signature.
+ @deprecated Shared user id's cause non-deterministic behaviour within the
+ package manager. As such, it's use is discouraged, deprecated, and will
+ be removed altogether in a future version of Android. Instead, proper
+ communication mechanisms such as services and providers should be used
+ to facilitate interoperability between shared components. -->
<attr name="sharedUserId" format="string" />
<!-- Specify a label for the shared user UID of this package. This is
only used if you have also used android:sharedUserId. This must
be a reference to a string resource; it can not be an explicit
- string. -->
+ string.
+ @deprecated There is no replacement for this attribute.
+ {@link android.R.attr#sharedUserId} has been deprecated making
+ this attribute unnecessary. -->
<attr name="sharedUserLabel" format="reference" />
<!-- Internal version code. This is the number used to determine whether
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 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/config.xml b/core/res/res/values/config.xml
index b3e94e3..21a8f4c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -572,6 +572,15 @@
<!-- Integer size limit, in KB, for a single WifiLogger ringbuffer, in verbose logging mode -->
<integer translatable="false" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb">1024</integer>
+ <!-- Array indicating wifi fatal firmware alert error code list from driver -->
+ <integer-array translatable="false" name="config_wifi_fatal_firmware_alert_error_code_list">
+ <!-- Example:
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ -->
+ </integer-array>
+
<!-- Boolean indicating whether or not wifi should turn off when emergency call is made -->
<bool translatable="false" name="config_wifi_turn_off_during_emergency_call">false</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 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/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 67a8ccc..470c9ed 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -380,6 +380,7 @@
<java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
<java-symbol type="integer" name="config_wifi_logger_ring_buffer_default_size_limit_kb" />
<java-symbol type="integer" name="config_wifi_logger_ring_buffer_verbose_size_limit_kb" />
+ <java-symbol type="array" name="config_wifi_fatal_firmware_alert_error_code_list" />
<java-symbol type="bool" name="config_wifi_turn_off_during_emergency_call" />
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
@@ -1745,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" />
@@ -2529,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" />
@@ -2782,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" />
@@ -3758,4 +3766,8 @@
<java-symbol type="drawable" name="perm_group_camera" />
<java-symbol type="drawable" name="perm_group_location" />
<java-symbol type="drawable" name="perm_group_microphone" />
+
+ <java-symbol type="drawable" name="chooser_direct_share_icon_placeholder" />
+ <java-symbol type="color" name="chooser_gradient_background" />
+ <java-symbol type="color" name="chooser_gradient_highlight" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 03fb1fc..0ed9860 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -802,6 +802,8 @@
<item name="textAppearanceListItemSmall">@style/TextAppearance.DeviceDefault.ListItem</item>
<item name="textAppearanceListItemSecondary">@style/TextAppearance.DeviceDefault.ListItemSecondary</item>
+ <item name="backgroundDimAmount">0.7</item>
+
<!-- Button styles -->
<item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonStyle">@style/Widget.DeviceDefault.Light.Button</item>
diff --git a/core/tests/coretests/res/values/overlayable_icons_test.xml b/core/tests/coretests/res/values/overlayable_icons_test.xml
new file mode 100644
index 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/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
new file mode 100644
index 0000000..c6f4fa2
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * Unit test for {@link ContentCaptureOptions}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:android.content.ContentCaptureOptionsTest}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ContentCaptureOptionsTest {
+
+ private final ComponentName mContextComponent = new ComponentName("marco", "polo");
+ private final ComponentName mComp1 = new ComponentName("comp", "one");
+ private final ComponentName mComp2 = new ComponentName("two", "comp");
+
+ @Mock private Context mContext;
+ @Mock private ContentCaptureClient mClient;
+
+ @Before
+ public void setExpectation() {
+ when(mClient.contentCaptureClientGetComponentName()).thenReturn(mContextComponent);
+ when(mContext.getContentCaptureClient()).thenReturn(mClient);
+ }
+
+ @Test
+ public void testIsWhitelisted_nullWhitelistedComponents() {
+ ContentCaptureOptions options = new ContentCaptureOptions(null);
+ assertThat(options.isWhitelisted(mContext)).isTrue();
+ }
+
+ @Test
+ public void testIsWhitelisted_emptyWhitelistedComponents() {
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet((ComponentName) null));
+ assertThat(options.isWhitelisted(mContext)).isFalse();
+ }
+
+ @Test
+ public void testIsWhitelisted_notWhitelisted() {
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mComp2));
+ assertThat(options.isWhitelisted(mContext)).isFalse();
+ }
+
+ @Test
+ public void testIsWhitelisted_whitelisted() {
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mContextComponent));
+ assertThat(options.isWhitelisted(mContext)).isTrue();
+ }
+
+ @Test
+ public void testIsWhitelisted_invalidContext() {
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent));
+ Context invalidContext = mock(Context.class); // has no client
+ assertThat(options.isWhitelisted(invalidContext)).isFalse();
+ }
+
+ @Test
+ public void testIsWhitelisted_clientWithNullComponentName() {
+ ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent));
+ ContentCaptureClient client = mock(ContentCaptureClient.class);
+ Context context = mock(Context.class);
+ when(context.getContentCaptureClient()).thenReturn(client);
+
+ assertThat(options.isWhitelisted(context)).isFalse();
+ }
+
+ @NonNull
+ private ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) {
+ ArraySet<ComponentName> set = new ArraySet<>();
+ if (comps != null) {
+ for (int i = 0; i < comps.length; i++) {
+ set.add(comps[i]);
+ }
+ }
+ return set;
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a853121..f4d3c81 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -392,6 +392,7 @@
Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS,
Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
Settings.Global.POLICY_CONTROL,
+ Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
Settings.Global.POWER_MANAGER_CONSTANTS,
Settings.Global.PREFERRED_NETWORK_MODE,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e4a93e7..dbbe1b4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -41,6 +41,7 @@
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
</privapp-permissions>
<privapp-permissions package="com.android.defcontainer">
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 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/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index cf5d7ce..0a9d965 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -45,12 +45,12 @@
1920, // viewportH
};
-const DeviceInfo* DeviceInfo::get() {
+DeviceInfo* DeviceInfo::get() {
static DeviceInfo sDeviceInfo;
return &sDeviceInfo;
}
-DisplayInfo QueryDisplayInfo() {
+static DisplayInfo QueryDisplayInfo() {
if (Properties::isolatedProcess) {
return sDummyDisplay;
}
@@ -65,6 +65,27 @@
return displayInfo;
}
+static float QueryMaxRefreshRate() {
+ if (Properties::isolatedProcess) {
+ return sDummyDisplay.fps;
+ }
+
+ const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
+ LOG_ALWAYS_FATAL_IF(token == nullptr,
+ "Failed to get display info because internal display is disconnected");
+
+ Vector<DisplayInfo> configs;
+ configs.reserve(10);
+ status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+ LOG_ALWAYS_FATAL_IF(status, "Failed to getDisplayConfigs, error %d", status);
+ LOG_ALWAYS_FATAL_IF(configs.size() == 0, "getDisplayConfigs returned 0 configs?");
+ float max = 0.0f;
+ for (auto& info : configs) {
+ max = std::max(max, info.fps);
+ }
+ return max;
+}
+
static void queryWideColorGamutPreference(sk_sp<SkColorSpace>* colorSpace, SkColorType* colorType) {
if (Properties::isolatedProcess) {
*colorSpace = SkColorSpace::MakeSRGB();
@@ -103,7 +124,7 @@
}
}
-DeviceInfo::DeviceInfo() {
+DeviceInfo::DeviceInfo() : mMaxRefreshRate(QueryMaxRefreshRate()) {
#if HWUI_NULL_GPU
mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
#else
@@ -119,7 +140,11 @@
}
void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
- const_cast<DeviceInfo*>(DeviceInfo::get())->mMaxTextureSize = maxTextureSize;
+ DeviceInfo::get()->mMaxTextureSize = maxTextureSize;
+}
+
+void DeviceInfo::onDisplayConfigChanged() {
+ mDisplayInfo = QueryDisplayInfo();
}
} /* namespace uirenderer */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 2bab5d3..0e3f119 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -32,7 +32,7 @@
PREVENT_COPY_AND_ASSIGN(DeviceInfo);
public:
- static const DeviceInfo* get();
+ static DeviceInfo* get();
// this value is only valid after the GPU has been initialized and there is a valid graphics
// context or if you are using the HWUI_NULL_GPU
@@ -40,6 +40,9 @@
const DisplayInfo& displayInfo() const { return mDisplayInfo; }
sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; }
SkColorType getWideColorType() const { return mWideColorType; }
+ float getMaxRefreshRate() const { return mMaxRefreshRate; }
+
+ void onDisplayConfigChanged();
private:
friend class renderthread::RenderThread;
@@ -51,6 +54,7 @@
DisplayInfo mDisplayInfo;
sk_sp<SkColorSpace> mWideColorSpace;
SkColorType mWideColorType;
+ const float mMaxRefreshRate;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 9998854..19f509c 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -67,7 +67,7 @@
bool Properties::isolatedProcess = false;
int Properties::contextPriority = 0;
-uint32_t Properties::defaultRenderAhead = 0;
+int Properties::defaultRenderAhead = -1;
static int property_get_int(const char* key, int defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {
@@ -130,9 +130,8 @@
enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true);
- defaultRenderAhead =
- std::max(0u, std::min(2u, static_cast<uint32_t>(property_get_int(
- PROPERTY_RENDERAHEAD, render_ahead().value_or(0)))));
+ defaultRenderAhead = std::max(-1, std::min(2, property_get_int(PROPERTY_RENDERAHEAD,
+ render_ahead().value_or(0))));
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3105e58..3e91c63 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -253,7 +253,7 @@
ANDROID_API static int contextPriority;
- static uint32_t defaultRenderAhead;
+ static int defaultRenderAhead;
private:
static ProfileType sProfileType;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 7cb241d..1b638c1 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -126,6 +126,10 @@
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
}
+
+ // We must sync the cpu to make sure deletions of resources still queued up on the GPU actually
+ // happen.
+ mGrContext->flush(kSyncCpu_GrFlushFlag, 0, nullptr);
}
void CacheManager::trimStaleResources() {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2957b14..f326ce8 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -105,13 +105,13 @@
, mGenerationID(0)
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
- , mJankTracker(&thread.globalProfileData(), thread.mainDisplayInfo())
+ , mJankTracker(&thread.globalProfileData(), DeviceInfo::get()->displayInfo())
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline)) {
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
- mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
+ mProfiler.setDensity(DeviceInfo::get()->displayInfo().density);
setRenderAheadDepth(Properties::defaultRenderAhead);
}
@@ -153,16 +153,23 @@
mNativeSurface = nullptr;
}
+ if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
+ mFixedRenderAhead = false;
+ mRenderAheadCapacity = 1;
+ } else {
+ mFixedRenderAhead = true;
+ mRenderAheadCapacity = mRenderAheadDepth;
+ }
+
ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode,
- mRenderAheadDepth);
+ mRenderAheadCapacity);
mFrameNumber = -1;
if (hasSurface) {
mHaveNewSurface = true;
mSwapHistory.clear();
- applyRenderAheadSettings();
} else {
mRenderThread.removeFrameCallback(this);
mGenerationID++;
@@ -403,6 +410,23 @@
mRenderThread.pushBackFrameCallback(this);
}
+void CanvasContext::setPresentTime() {
+ int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
+ int renderAhead = 0;
+ const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos();
+ if (mFixedRenderAhead) {
+ renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity);
+ } else if (frameIntervalNanos < 15_ms) {
+ renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity));
+ }
+
+ if (renderAhead) {
+ presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
+ (frameIntervalNanos * (renderAhead + 1));
+ }
+ native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime);
+}
+
void CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -415,14 +439,9 @@
mCurrentFrameInfo->markIssueDrawCommandsStart();
Frame frame = mRenderPipeline->getFrame();
+ setPresentTime();
SkRect windowDirty = computeDirtyRect(frame, &dirty);
- if (mRenderAheadDepth) {
- auto presentTime =
- mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
- (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1));
- native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime);
- }
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
@@ -656,18 +675,12 @@
return width == mLastFrameWidth && height == mLastFrameHeight;
}
-void CanvasContext::applyRenderAheadSettings() {
- if (mNativeSurface && !mRenderAheadDepth) {
- native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO);
- }
-}
-
-void CanvasContext::setRenderAheadDepth(uint32_t renderAhead) {
- if (renderAhead > 2 || renderAhead == mRenderAheadDepth || mNativeSurface) {
+void CanvasContext::setRenderAheadDepth(int renderAhead) {
+ if (renderAhead > 2 || renderAhead < 0 || mNativeSurface) {
return;
}
- mRenderAheadDepth = renderAhead;
- applyRenderAheadSettings();
+ mFixedRenderAhead = true;
+ mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
}
SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 912b125..f9de002 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -204,7 +204,7 @@
}
// Must be called before setSurface
- void setRenderAheadDepth(uint32_t renderAhead);
+ void setRenderAheadDepth(int renderAhead);
SkISize getNextFrameSize() const;
@@ -221,7 +221,7 @@
bool isSwapChainStuffed();
bool surfaceRequiresRedraw();
- void applyRenderAheadSettings();
+ void setPresentTime();
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
@@ -240,7 +240,9 @@
// painted onto its surface.
bool mIsDirty = false;
SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
+ bool mFixedRenderAhead = false;
uint32_t mRenderAheadDepth = 0;
+ uint32_t mRenderAheadCapacity = 0;
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index eca7d88..41cb8fd 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -60,8 +60,10 @@
class DisplayEventReceiverWrapper : public VsyncSource {
public:
- DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver)
- : mDisplayEventReceiver(std::move(receiver)) {}
+ DisplayEventReceiverWrapper(std::unique_ptr<DisplayEventReceiver>&& receiver,
+ const std::function<void()>& onDisplayConfigChanged)
+ : mDisplayEventReceiver(std::move(receiver))
+ , mOnDisplayConfigChanged(onDisplayConfigChanged) {}
virtual void requestNextVsync() override {
status_t status = mDisplayEventReceiver->requestNextVsync();
@@ -79,6 +81,9 @@
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
latest = ev.header.timestamp;
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ mOnDisplayConfigChanged();
+ break;
}
}
}
@@ -90,6 +95,7 @@
private:
std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
+ std::function<void()> mOnDisplayConfigChanged;
};
class DummyVsyncSource : public VsyncSource {
@@ -160,22 +166,29 @@
// Register the FD
mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
RenderThread::displayEventReceiverCallback, this);
- mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
+ mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver), [this] {
+ DeviceInfo::get()->onDisplayConfigChanged();
+ setupFrameInterval();
+ });
} else {
mVsyncSource = new DummyVsyncSource(this);
}
}
void RenderThread::initThreadLocals() {
- mDisplayInfo = DeviceInfo::get()->displayInfo();
- nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
- mTimeLord.setFrameInterval(frameIntervalNanos);
- mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f);
+ setupFrameInterval();
initializeDisplayEventReceiver();
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager();
- mCacheManager = new CacheManager(mDisplayInfo);
+ mCacheManager = new CacheManager(DeviceInfo::get()->displayInfo());
+}
+
+void RenderThread::setupFrameInterval() {
+ auto& displayInfo = DeviceInfo::get()->displayInfo();
+ nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / displayInfo.fps);
+ mTimeLord.setFrameInterval(frameIntervalNanos);
+ mDispatchFrameDelay = static_cast<nsecs_t>(frameIntervalNanos * .25f);
}
void RenderThread::requireGlContext() {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 6bb26fd..c96e284 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -102,8 +102,6 @@
ProfileDataContainer& globalProfileData() { return mGlobalProfileData; }
Readback& readback();
- const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
-
GrContext* getGrContext() const { return mGrContext.get(); }
void setGrContext(sk_sp<GrContext> cxt);
@@ -149,13 +147,12 @@
void initThreadLocals();
void initializeDisplayEventReceiver();
+ void setupFrameInterval();
static int displayEventReceiverCallback(int fd, int events, void* data);
void drainDisplayEventQueue();
void dispatchFrameCallbacks();
void requestVsync();
- DisplayInfo mDisplayInfo;
-
VsyncSource* mVsyncSource;
bool mVsyncRequested;
std::set<IFrameCallback*> mFrameCallbacks;
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index 210fced..3f1ef93 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -33,7 +33,7 @@
}
RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
- DisplayInfo displayInfo = renderThread.mainDisplayInfo();
+ DisplayInfo displayInfo = DeviceInfo::get()->displayInfo();
GrContext* grContext = renderThread.getGrContext();
ASSERT_TRUE(grContext != nullptr);
diff --git a/libs/protoutil/src/ProtoFileReader.cpp b/libs/protoutil/src/ProtoFileReader.cpp
index 4017979..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++];
}
@@ -127,8 +128,10 @@
if (!ensure_data()) {
return;
}
- const size_t chunk = mMaxOffset - mOffset < amt ? amt : mMaxOffset - mOffset;
+ const size_t chunk =
+ mMaxOffset - mOffset > amt ? amt : mMaxOffset - mOffset;
mOffset += chunk;
+ mPos += chunk;
amt -= chunk;
}
}
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index badffd1..36fe8da 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -16,12 +16,8 @@
package android.location;
-import android.annotation.IntDef;
import android.annotation.SystemApi;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* A container of supported GNSS chipset capabilities.
*
@@ -29,53 +25,61 @@
*/
@SystemApi
public final class GnssCapabilities {
- /** The GNSS chipset supports low power mode. */
- public static final int LOW_POWER_MODE = 0;
-
- /** The GNSS chipset supports blacklisting satellites. */
- public static final int SATELLITE_BLACKLIST = 1;
-
- /** The GNSS chipset supports geofencing. */
- public static final int GEOFENCING = 2;
-
- /** The GNSS chipset supports measurements.*/
- public static final int MEASUREMENTS = 3;
-
- /** The GNSS chipset supports navigation messages. */
- public static final int NAV_MESSAGES = 4;
-
- /** The GNSS chipset supports measurement corrections. */
- public static final int MEASUREMENT_CORRECTIONS = 5;
-
- /** The GNSS chipset supports line-of-sight satellite identification measurement corrections. */
- public static final int MEASUREMENT_CORRECTIONS_LOS_SATS = 6;
-
- /** The GNSS chipset supports per satellite excess-path-length measurement corrections. */
- public static final int MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 7;
-
- /** The GNSS chipset supports reflecting planes measurement corrections. */
- public static final int MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 8;
-
- private static final int MIN_CAPABILITY = 0;
- private static final int MAX_CAPABILITY = MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
-
/**
- * GNSS capability.
+ * Bit mask indicating GNSS chipset supports low power mode.
* @hide
*/
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- LOW_POWER_MODE,
- SATELLITE_BLACKLIST,
- GEOFENCING,
- MEASUREMENTS,
- NAV_MESSAGES,
- MEASUREMENT_CORRECTIONS,
- MEASUREMENT_CORRECTIONS_LOS_SATS,
- MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH,
- MEASUREMENT_CORRECTIONS_REFLECTING_PLANE
- })
- public @interface Capability {}
+ public static final long LOW_POWER_MODE = 1L << 0;
+
+ /**
+ * Bit mask indicating GNSS chipset supports blacklisting satellites.
+ * @hide
+ */
+ public static final long SATELLITE_BLACKLIST = 1L << 1;
+
+ /**
+ * Bit mask indicating GNSS chipset supports geofencing.
+ * @hide
+ */
+ public static final long GEOFENCING = 1L << 2;
+
+ /**
+ * Bit mask indicating GNSS chipset supports measurements.
+ * @hide
+ */
+ public static final long MEASUREMENTS = 1L << 3;
+
+ /**
+ * Bit mask indicating GNSS chipset supports navigation messages.
+ * @hide
+ */
+ public static final long NAV_MESSAGES = 1L << 4;
+
+ /**
+ * Bit mask indicating GNSS chipset supports measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS = 1L << 5;
+
+ /**
+ * Bit mask indicating GNSS chipset supports line-of-sight satellite identification
+ * measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS_LOS_SATS = 1L << 6;
+
+ /**
+ * Bit mask indicating GNSS chipset supports per satellite excess-path-length
+ * measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 1L << 7;
+
+ /**
+ * Bit mask indicating GNSS chipset supports reflecting planes measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 1L << 8;
/** @hide */
public static final long INVALID_CAPABILITIES = -1;
@@ -93,60 +97,94 @@
}
/**
- * Returns {@code true} if the {@code capability} is supported by the GNSS implementation.
+ * Returns {@code true} if GNSS chipset supports low power mode, {@code false} otherwise.
*/
- public boolean hasCapability(@Capability int capability) {
- return isValidCapability(capability) && (mGnssCapabilities & (1 << capability)) != 0;
+ public boolean hasLowPowerMode() {
+ return hasCapability(LOW_POWER_MODE);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports blacklisting satellites, {@code false}
+ * otherwise.
+ */
+ public boolean hasSatelliteBlacklist() {
+ return hasCapability(SATELLITE_BLACKLIST);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports geofencing, {@code false} otherwise.
+ */
+ public boolean hasGeofencing() {
+ return hasCapability(GEOFENCING);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports measurements, {@code false} otherwise.
+ */
+ public boolean hasMeasurements() {
+ return hasCapability(MEASUREMENTS);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports navigation messages, {@code false} otherwise.
+ */
+ public boolean hasNavMessages() {
+ return hasCapability(NAV_MESSAGES);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports measurement corrections, {@code false}
+ * otherwise.
+ */
+ public boolean hasMeasurementCorrections() {
+ return hasCapability(MEASUREMENT_CORRECTIONS);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports line-of-sight satellite identification
+ * measurement corrections, {@code false} otherwise.
+ */
+ public boolean hasMeasurementCorrectionsLosSats() {
+ return hasCapability(MEASUREMENT_CORRECTIONS_LOS_SATS);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports per satellite excess-path-length measurement
+ * corrections, {@code false} otherwise.
+ */
+ public boolean hasMeasurementCorrectionsExcessPathLength() {
+ return hasCapability(MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports reflecting planes measurement corrections,
+ * {@code false} otherwise.
+ */
+ public boolean hasMeasurementCorrectionsReflectingPane() {
+ return hasCapability(MEASUREMENT_CORRECTIONS_REFLECTING_PLANE);
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("GnssCapabilities: (");
- int capability = 0;
- boolean addSeparator = false;
- long gnssCapabilities = mGnssCapabilities;
- while (gnssCapabilities != 0) {
- if ((gnssCapabilities & 1) != 0) {
- if (addSeparator) {
- sb.append(' ');
- } else {
- addSeparator = true;
- }
- sb.append(toStringCapability(capability));
- }
- gnssCapabilities >>= 1;
- ++capability;
+ StringBuilder sb = new StringBuilder("GnssCapabilities: ( ");
+ if (hasLowPowerMode()) sb.append("LOW_POWER_MODE ");
+ if (hasSatelliteBlacklist()) sb.append("SATELLITE_BLACKLIST ");
+ if (hasGeofencing()) sb.append("GEOFENCING ");
+ if (hasMeasurements()) sb.append("MEASUREMENTS ");
+ if (hasNavMessages()) sb.append("NAV_MESSAGES ");
+ if (hasMeasurementCorrections()) sb.append("MEASUREMENT_CORRECTIONS ");
+ if (hasMeasurementCorrectionsLosSats()) sb.append("MEASUREMENT_CORRECTIONS_LOS_SATS ");
+ if (hasMeasurementCorrectionsExcessPathLength()) {
+ sb.append("MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH ");
+ }
+ if (hasMeasurementCorrectionsReflectingPane()) {
+ sb.append("MEASUREMENT_CORRECTIONS_REFLECTING_PLANE ");
}
sb.append(")");
return sb.toString();
}
- private boolean isValidCapability(@Capability int capability) {
- return capability >= MIN_CAPABILITY && capability <= MAX_CAPABILITY;
- }
-
- private static String toStringCapability(@Capability int capability) {
- switch (capability) {
- case LOW_POWER_MODE:
- return "LOW_POWER_MODE";
- case SATELLITE_BLACKLIST:
- return "SATELLITE_BLACKLIST";
- case GEOFENCING:
- return "GEOFENCING";
- case MEASUREMENTS:
- return "MEASUREMENTS";
- case NAV_MESSAGES:
- return "NAV_MESSAGES";
- case MEASUREMENT_CORRECTIONS:
- return "MEASUREMENT_CORRECTIONS";
- case MEASUREMENT_CORRECTIONS_LOS_SATS:
- return "MEASUREMENT_CORRECTIONS_LOS_SATS";
- case MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH:
- return "MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH";
- case MEASUREMENT_CORRECTIONS_REFLECTING_PLANE:
- return "MEASUREMENT_CORRECTIONS_REFLECTING_PLANE";
- default:
- return "Unknown(" + capability + ")";
- }
+ private boolean hasCapability(long capability) {
+ return (mGnssCapabilities & capability) == capability;
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index af60e3c..7a17505 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1986,17 +1986,19 @@
}
/**
- * Returns the supported capabilities of the GNSS chipset or {@code null} if there is an error
- * in obtaining the capabilities.
+ * Returns the supported capabilities of the GNSS chipset.
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present.
*
* @hide
*/
@SystemApi
- public @Nullable GnssCapabilities getGnssCapabilities() {
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public @NonNull GnssCapabilities getGnssCapabilities() {
try {
long gnssCapabilities = mGnssMeasurementCallbackTransport.getGnssCapabilities();
if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) {
- return null;
+ gnssCapabilities = 0L;
}
return GnssCapabilities.of(gnssCapabilities);
} catch (RemoteException e) {
diff --git a/media/java/android/media/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/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 9a074dd..728a239 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/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index efebfc2..8f8fd11 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -16,11 +16,16 @@
package com.android.systemui.statusbar.car;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.car.drivingstate.CarDrivingStateEvent;
+import android.content.Context;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.GestureDetector;
@@ -34,10 +39,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;
@@ -55,6 +63,7 @@
import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.car.hvac.TemperatureView;
@@ -74,6 +83,15 @@
public class CarStatusBar extends StatusBar implements
CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
+ // used to calculate how fast to open or close the window
+ private static final float DEFAULT_FLING_VELOCITY = 0;
+ // max time a fling animation takes
+ private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
+ // acceleration rate for the fling animation
+ private static final float FLING_SPEED_UP_FACTOR = 0.6f;
+
+ private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
private TaskStackListenerImpl mTaskStackListener;
@@ -100,6 +118,7 @@
private boolean mDeviceIsProvisioned = true;
private HvacController mHvacController;
private DrivingStateHelper mDrivingStateHelper;
+ private static FlingAnimationUtils sFlingAnimationUtils;
private SwitchToGuestTimer mSwitchToGuestTimer;
// The container for the notifications.
@@ -113,6 +132,27 @@
// it's open.
private View.OnTouchListener mNavBarNotificationTouchListener;
+ // Percentage from top of the screen after which the notification shade will open. This value
+ // will be used while opening the notification shade.
+ private int mSettleOpenPercentage;
+ // Percentage from top of the screen below which the notification shade will close. This
+ // value will be used while closing the notification shade.
+ private int mSettleClosePercentage;
+ // Percentage of notification shade open from top of the screen.
+ private int mPercentageFromBottom;
+ // If notification shade is animation to close or to open.
+ private boolean mIsNotificationAnimating;
+
+ // Tracks when the notification shade is being scrolled. This refers to the glass pane being
+ // scrolled not the recycler view.
+ private boolean mIsTracking;
+ private float mFirstTouchDownOnGlassPane;
+
+ // If the notification card inside the recycler view is being swiped.
+ private boolean mIsNotificationCardSwiping;
+ // If notification shade is being swiped vertically to close.
+ private boolean mIsSwipingVerticallyToClose;
+
@Override
public void start() {
// get the provisioned state before calling the parent class since it's that flow that
@@ -125,6 +165,12 @@
mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
mNotificationPanel.setScrollingEnabled(true);
+ mSettleOpenPercentage = mContext.getResources().getInteger(
+ R.integer.notification_settle_open_percentage);
+ mSettleClosePercentage = mContext.getResources().getInteger(
+ R.integer.notification_settle_close_percentage);
+ sFlingAnimationUtils = new FlingAnimationUtils(mContext,
+ FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
createBatteryController();
mCarBatteryController.startListening();
@@ -313,14 +359,46 @@
}
});
mNavBarNotificationTouchListener =
- (v, event) -> navBarCloseNotificationGestureDetector.onTouchEvent(event);
+ (v, event) -> {
+ boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ && mNotificationView.getVisibility() == View.VISIBLE) {
+ if (mSettleClosePercentage < mPercentageFromBottom) {
+ animateNotificationPanel(
+ DEFAULT_FLING_VELOCITY, false);
+ } else {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY,
+ true);
+ }
+ }
+ return true;
+ };
// The following are the ui elements that the user would call the status bar.
// This will set the status bar so it they can make call backs.
CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
topBar.setStatusBar(this);
- topBar.setStatusBarWindowTouchListener((v1, event1) ->
- openGestureDetector.onTouchEvent(event1));
+ topBar.setStatusBarWindowTouchListener((v1, event1) -> {
+
+ boolean consumed = openGestureDetector.onTouchEvent(event1);
+ if (consumed) {
+ return true;
+ }
+ if (event1.getActionMasked() == MotionEvent.ACTION_UP
+ && mNotificationView.getVisibility() == View.VISIBLE) {
+ if (mSettleOpenPercentage > mPercentageFromBottom) {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ } else {
+ animateNotificationPanel(
+ DEFAULT_FLING_VELOCITY, false);
+ }
+ }
+ return true;
+ }
+ );
NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
mBarService,
@@ -333,8 +411,13 @@
CarNotificationListener carNotificationListener = new CarNotificationListener();
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
new CarUxRestrictionManagerWrapper();
+ 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);
@@ -347,7 +430,10 @@
mNotificationListAtBottomAtTimeOfTouch = false;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFirstTouchDownOnGlassPane = event.getRawX();
mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
+ // Reset the tracker when there is a touch down on the glass pane.
+ mIsTracking = false;
// Pass the down event to gesture detector so that it knows where the touch event
// started.
closeGestureDetector.onTouchEvent(event);
@@ -364,23 +450,59 @@
return;
}
mNotificationListAtBottom = false;
+ mIsSwipingVerticallyToClose = false;
mNotificationListAtBottomAtTimeOfTouch = false;
}
});
mNotificationList.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
+ mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
+ > SWIPE_MAX_OFF_PATH;
+ if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
+ // We need to save the state here as if notification card is swiping we will
+ // change the mNotificationListAtBottomAtTimeOfTouch. This is to protect
+ // closing the notification shade while the notification card is being swiped.
+ mIsSwipingVerticallyToClose = true;
+ }
+
+ // If the card is swiping we should not allow the notification shade to close.
+ // Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that
+ // for us. We are also checking for mIsTracking because while swiping the
+ // notification shade to close if the user goes a bit horizontal while swiping
+ // upwards then also this should close.
+ if (mIsNotificationCardSwiping && !mIsTracking) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+
boolean handled = false;
if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
handled = closeGestureDetector.onTouchEvent(event);
}
+ boolean isTracking = mIsTracking;
+ Rect rect = mNotificationList.getClipBounds();
+ float clippedHeight = 0;
+ if (rect != null) {
+ clippedHeight = rect.bottom;
+ }
+ if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
+ && mIsSwipingVerticallyToClose) {
+ if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
+ } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
+ // this can be caused when user is at the end of the list and trying to
+ // fling to top of the list by scrolling down.
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ }
+
// Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
// the event has been passed to the closeGestureDetector above, such that the
// closeGestureDetector sees the up event before the state has changed.
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
mNotificationListAtBottomAtTimeOfTouch = false;
}
- return handled;
+ return handled || isTracking;
}
});
@@ -401,7 +523,9 @@
mNotificationList.scrollToPosition(0);
mStatusBarWindowController.setPanelVisible(true);
mNotificationView.setVisibility(View.VISIBLE);
- // let the status bar know that the panel is open
+
+ animateNotificationPanel(mOpeningVelocity, false);
+
setPanelExpanded(true);
}
@@ -415,12 +539,66 @@
mStatusBarWindowController.setStatusBarFocusable(false);
mStatusBarWindow.cancelExpandHelper();
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
- mStatusBarWindowController.setPanelVisible(false);
- mNotificationView.setVisibility(View.INVISIBLE);
- // let the status bar know that the panel is cloased
+
+ animateNotificationPanel(mClosingVelocity, true);
+
+ if (!mIsTracking) {
+ mStatusBarWindowController.setPanelVisible(false);
+ mNotificationView.setVisibility(View.INVISIBLE);
+ }
+
setPanelExpanded(false);
}
+ /**
+ * Animates the notification shade from one position to other. This is used to either open or
+ * close the notification shade completely with a velocity. If the animation is to close the
+ * notification shade this method also makes the view invisible after animation ends.
+ */
+ private void animateNotificationPanel(float velocity, boolean isClosing) {
+ Rect rect = mNotificationList.getClipBounds();
+ if (rect == null) {
+ return;
+ }
+ float from = rect.bottom;
+ float to = 0;
+ if (!isClosing) {
+ to = mNotificationView.getHeight();
+ }
+ if (mIsNotificationAnimating) {
+ return;
+ }
+ mIsNotificationAnimating = true;
+ mIsTracking = true;
+ ValueAnimator animator = ValueAnimator.ofFloat(from, to);
+ animator.addUpdateListener(
+ animation -> {
+ float animatedValue = (Float) animation.getAnimatedValue();
+ setNotificationViewClipBounds((int) animatedValue);
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mIsNotificationAnimating = false;
+ mIsTracking = false;
+ mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (isClosing) {
+ mStatusBarWindowController.setPanelVisible(false);
+ mNotificationView.setVisibility(View.INVISIBLE);
+ mNotificationList.setClipBounds(null);
+ // let the status bar know that the panel is closed
+ setPanelExpanded(false);
+ } else {
+ // let the status bar know that the panel is open
+ setPanelExpanded(true);
+ }
+ }
+ });
+ sFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
+ animator.start();
+ }
@Override
protected QS createDefaultQSFragment() {
@@ -576,7 +754,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- //When executing dump() funciton simultaneously, we need to serialize them
+ //When executing dump() function simultaneously, we need to serialize them
//to get mStackScroller's position correctly.
synchronized (mQueueLock) {
pw.println(" mStackScroller: " + viewInfo(mStackScroller));
@@ -760,38 +938,80 @@
}
/** Returns true if the current user makes it through the setup wizard, false otherwise. */
- public boolean getIsUserSetup(){
+ private boolean getIsUserSetup() {
return mUserSetup;
}
+ private void setNotificationViewClipBounds(int height) {
+ Rect clipBounds = new Rect();
+ clipBounds.set(0, 0, mNotificationView.getWidth(), height);
+ // Sets the clip region on the notification list view.
+ mNotificationList.setClipBounds(clipBounds);
- // TODO: add settle down/up logic
+ if (mNotificationView.getHeight() > 0) {
+ // Calculates the alpha value for the background based on how much of the notification
+ // shade is visible to the user. When the notification shade is completely open then
+ // alpha value will be 1.
+ float alpha = (float) height / mNotificationView.getHeight();
+ Drawable background = mNotificationView.getBackground();
+
+ background.setAlpha((int) (alpha * 255));
+ }
+ }
+
+ private void calculatePercentageFromBottom(float height) {
+ if (mNotificationView.getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ height / mNotificationView.getHeight() * 100);
+ }
+ }
+
private static final int SWIPE_UP_MIN_DISTANCE = 75;
private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
private static final int SWIPE_MAX_OFF_PATH = 75;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+
// Only responsible for open hooks. Since once the panel opens it covers all elements
// there is no need to merge with close.
private abstract class OpenNotificationGestureListener extends
GestureDetector.SimpleOnGestureListener {
@Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+
+ if (mNotificationView.getVisibility() == View.INVISIBLE) {
+ // when the on-scroll is called for the first time to open.
+ mNotificationList.scrollToPosition(0);
+ }
+ mStatusBarWindowController.setPanelVisible(true);
+ mNotificationView.setVisibility(View.VISIBLE);
+
+ // clips the view for the notification shade when the user scrolls to open.
+ setNotificationViewClipBounds((int) event2.getRawY());
+
+ // Initially the scroll starts with height being zero. This checks protects from divide
+ // by zero error.
+ calculatePercentageFromBottom(event2.getRawY());
+
+ mIsTracking = true;
+ return true;
+ }
+
+
+ @Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
- if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
- || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
- // swipe was not vertical or was not fast enough
- return false;
- }
- boolean isDown = velocityY > 0;
- float distanceDelta = Math.abs(event1.getY() - event2.getY());
- if (isDown && distanceDelta > SWIPE_DOWN_MIN_DISTANCE) {
+ if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
+ mOpeningVelocity = velocityY;
openNotification();
return true;
}
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
return false;
}
+
protected abstract void openNotification();
}
@@ -800,37 +1020,130 @@
GestureDetector.SimpleOnGestureListener {
@Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ if (!mNotificationListAtBottomAtTimeOfTouch) {
+ return false;
+ }
+ float actualNotificationHeight =
+ mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
+ if (actualNotificationHeight > mNotificationView.getHeight()) {
+ actualNotificationHeight = mNotificationView.getHeight();
+ }
+ if (mNotificationView.getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ actualNotificationHeight / mNotificationView.getHeight() * 100);
+ boolean isUp = distanceY > 0;
+
+ // This check is to figure out if onScroll was called while swiping the card at
+ // bottom of the list. At that time we should not allow notification shade to
+ // close. We are also checking for the upwards swipe gesture here because it is
+ // possible if a user is closing the notification shade and while swiping starts
+ // to open again but does not fling. At that time we should allow the
+ // notification shade to close fully or else it would stuck in between.
+ if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
+ > SWIPE_DOWN_MIN_DISTANCE && isUp) {
+ setNotificationViewClipBounds((int) actualNotificationHeight);
+ mIsTracking = true;
+ } else if (!isUp) {
+ setNotificationViewClipBounds((int) actualNotificationHeight);
+ }
+ }
+ // if we return true the the items in RV won't be scrollable.
+ return false;
+ }
+
+
+ @Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
+
if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
|| Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
// swipe was not vertical or was not fast enough
return false;
}
boolean isUp = velocityY < 0;
- float distanceDelta = Math.abs(event1.getY() - event2.getY());
- if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
+ if (isUp) {
close();
return true;
+ } else {
+ // we should close the shade
+ animateNotificationPanel(velocityY, false);
}
return false;
}
+
protected abstract void close();
}
- // to be installed on the nav bars
+ // To be installed on the nav bars.
private abstract class NavBarCloseNotificationGestureListener extends
CloseNotificationGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
close();
return super.onSingleTapUp(e);
}
@Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ calculatePercentageFromBottom(event2.getRawY());
+ setNotificationViewClipBounds((int) event2.getRawY());
+ return true;
+ }
+
+ @Override
public void onLongPress(MotionEvent e) {
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
close();
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/DynamicSystemInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
similarity index 100%
rename from packages/DynamicSystemInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
rename to packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
similarity index 100%
rename from packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
rename to packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
similarity index 100%
rename from packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
rename to packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
similarity index 100%
rename from packages/DynamicSystemInstallationService/src/com/android/dynandroid/VerificationActivity.java
rename to packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml
index 90f96e0..704788d 100644
--- a/packages/NetworkStack/res/values/config.xml
+++ b/packages/NetworkStack/res/values/config.xml
@@ -35,4 +35,8 @@
</string-array>
<string-array name="config_captive_portal_fallback_probe_specs" translatable="false">
</string-array>
-</resources>
\ No newline at end of file
+
+ <!-- Customized default DNS Servers address. -->
+ <string-array name="config_default_dns_servers" translatable="false">
+ </string-array>
+</resources>
diff --git a/packages/NetworkStack/res/values/overlayable.xml b/packages/NetworkStack/res/values/overlayable.xml
index 7eeeefc..23b8de6 100644
--- a/packages/NetworkStack/res/values/overlayable.xml
+++ b/packages/NetworkStack/res/values/overlayable.xml
@@ -20,6 +20,8 @@
<item type="string" name="config_captive_portal_http_url"/>
<item type="string" name="config_captive_portal_https_url"/>
<item type="array" name="config_captive_portal_fallback_urls"/>
+ <!-- Configuration value for DhcpResults -->
+ <item type="array" name="config_default_dns_servers"/>
</policy>
</overlayable>
</resources>
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
index af0e3bb..ca6c17a 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
@@ -45,6 +45,7 @@
import android.content.Context;
import android.net.DhcpResults;
+import android.net.InetAddresses;
import android.net.TrafficStats;
import android.net.ip.IpClient;
import android.net.metrics.DhcpClientEvent;
@@ -67,6 +68,7 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.TrafficStatsConstants;
import com.android.internal.util.WakeupMessage;
+import com.android.networkstack.R;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -500,6 +502,18 @@
private void acceptDhcpResults(DhcpResults results, String msg) {
mDhcpLease = results;
+ if (mDhcpLease.dnsServers.isEmpty()) {
+ // supplement customized dns servers
+ String[] dnsServersList =
+ mContext.getResources().getStringArray(R.array.config_default_dns_servers);
+ for (final String dnsServer : dnsServersList) {
+ try {
+ mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid default DNS server: " + dnsServer, e);
+ }
+ }
+ }
mOffer = null;
Log.d(TAG, msg + " lease: " + mDhcpLease);
notifySuccess();
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index d21b5f7..b8ab94c 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -647,4 +647,9 @@
}
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 80d139c..96e09fa 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -557,6 +557,11 @@
checkNetworkStackCallingPermission();
IpClient.this.removeKeepalivePacketFilter(slot);
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
public String getInterfaceName() {
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 8226787..97e8186 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -35,6 +35,34 @@
// TODO: Refer to DeviceConfig definition.
public static final String NAMESPACE_CONNECTIVITY = "connectivity";
+ /**
+ * A list of captive portal detection specifications used in addition to the fallback URLs.
+ * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated
+ * by "@@,@@".
+ */
+ public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
+ "captive_portal_fallback_probe_specs";
+
+ /**
+ * A comma separated list of URLs used for captive portal detection in addition to the
+ * fallback HTTP url associated with the CAPTIVE_PORTAL_FALLBACK_URL settings.
+ */
+ public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
+ "captive_portal_other_fallback_urls";
+
+ /**
+ * Which User-Agent string to use in the header of the captive portal detection probes.
+ * The User-Agent field is unset when this setting has no value (HttpUrlConnection default).
+ */
+ public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
+
+ /**
+ * Whether to use HTTPS for network validation. This is enabled by default and the setting
+ * needs to be set to 0 to disable it. This setting is a misnomer because captive portals
+ * don't actually use HTTPS, but it's consistent with the other settings.
+ */
+ public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
+
static {
System.loadLibrary("networkstackutilsjni");
}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index a0a90fd..a6d7484 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -251,6 +251,11 @@
}
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
private static class NetworkMonitorImpl extends INetworkMonitor.Stub {
@@ -325,5 +330,10 @@
checkNetworkStackCallingPermission();
mNm.notifyNetworkCapabilitiesChanged(nc);
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 27d4203..093235e 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -43,6 +43,10 @@
import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS;
import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS;
import static android.net.util.DataStallUtils.DEFAULT_DNS_LOG_SIZE;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USER_AGENT;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
import static android.net.util.NetworkStackUtils.NAMESPACE_CONNECTIVITY;
import static android.net.util.NetworkStackUtils.isEmpty;
@@ -1171,7 +1175,8 @@
}
private boolean getUseHttpsValidation() {
- return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
+ return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
+ CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
}
private String getCaptivePortalServerHttpsUrl() {
@@ -1224,8 +1229,8 @@
final URL[] settingProviderUrls;
if (!TextUtils.isEmpty(firstUrl)) {
- final String otherUrls = mDependencies.getSetting(mContext,
- Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, "");
+ final String otherUrls = mDependencies.getDeviceConfigProperty(
+ NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, "");
// otherUrls may be empty, but .split() ignores trailing empty strings
final String separator = ",";
final String[] urls = (firstUrl + separator + otherUrls).split(separator);
@@ -1245,8 +1250,9 @@
private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() {
try {
- final String settingsValue = mDependencies.getSetting(
- mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
+ final String settingsValue = mDependencies.getDeviceConfigProperty(
+ NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
+
final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0];
final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue)
? emptySpecs
@@ -1340,8 +1346,8 @@
}
private String getCaptivePortalUserAgent() {
- return mDependencies.getSetting(mContext,
- Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
+ return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
+ CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
}
private URL nextFallbackUrl() {
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
index bee4bbd9..6a6bf83 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
@@ -494,4 +494,9 @@
listener.onComplete(makeStatus(ERROR_INTERNAL_INTERRUPTED));
return true;
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
index 2775fde..bea7052 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
@@ -91,6 +91,11 @@
}
@Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
public IBinder asBinder() {
return null;
}
diff --git a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
index 7d5e9e3..f0e2f1b 100644
--- a/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
@@ -133,6 +133,11 @@
public void onStatusAvailable(int statusCode) {
assertEquals(STATUS_SUCCESS, statusCode);
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
@Before
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 910bdc7..594f2ca 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -26,6 +26,9 @@
import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL;
import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
+import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -160,7 +163,7 @@
when(mDependencies.getRandom()).thenReturn(mRandom);
when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
.thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
- when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS),
+ when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS),
anyInt())).thenReturn(1);
when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
.thenReturn(TEST_HTTP_URL);
@@ -683,13 +686,13 @@
}
private void setOtherFallbackUrls(String urls) {
- when(mDependencies.getSetting(any(),
- eq(Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls);
+ when(mDependencies.getDeviceConfigProperty(any(),
+ eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls);
}
private void setFallbackSpecs(String specs) {
- when(mDependencies.getSetting(any(),
- eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
+ when(mDependencies.getDeviceConfigProperty(any(),
+ eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
}
private void setCaptivePortalMode(int mode) {
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index a00eff7..87346e5 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -136,6 +136,11 @@
public IBinder asBinder() {
return null;
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
@@ -156,6 +161,11 @@
public IBinder asBinder() {
return null;
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
@@ -178,6 +188,11 @@
public IBinder asBinder() {
return null;
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
@@ -200,6 +215,11 @@
public IBinder asBinder() {
return null;
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
@@ -219,6 +239,11 @@
public IBinder asBinder() {
return null;
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
diff --git a/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect1_copy.xml b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect1_copy.xml
new file mode 100644
index 0000000..28265ea
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect1_copy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect1 in frameworks/base/core/res -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="2000"
+ android:propertyXName="translateX"
+ android:pathData="M -522.59998,0 c 48.89972,0 166.02656,0 301.21729,0 c 197.58128,0 420.9827,0 420.9827,0 "
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_translatex_copy"
+ android:repeatCount="infinite" />
+ <objectAnimator
+ android:duration="2000"
+ android:propertyYName="scaleX"
+ android:pathData="M 0 0.1 L 1 0.826849212646 L 2 0.1"
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_scalex_copy"
+ android:repeatCount="infinite" />
+</set>
diff --git a/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect2_copy.xml b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect2_copy.xml
new file mode 100644
index 0000000..24cc452
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/anim/progress_indeterminate_horizontal_rect2_copy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect2 in frameworks/base/core/res -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="2000"
+ android:propertyXName="translateX"
+ android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0 "
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_translatex_copy"
+ android:repeatCount="infinite" />
+ <objectAnimator
+ android:duration="2000"
+ android:propertyYName="scaleX"
+ android:pathData="M 0.0,0.1 L 1.0,0.571379510698 L 2.0,0.909950256348 L 3.0,0.1"
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_scalex_copy"
+ android:repeatCount="infinite" />
+</set>
diff --git a/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
index 2b7535a..4bf639d 100644
--- a/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
+++ b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
@@ -14,15 +14,13 @@
limitations under the License.
-->
-<!-- Variant of progress_indeterminate_horizontal_material in frameworks/base/core/res, which
- draws the whole height of the progress bar instead having blank space above and below the
- bar. -->
+<!-- Copy of progress_indeterminate_horizontal_material_trimmed in frameworks/base/core/res -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
<target
android:name="rect2_grp"
- android:animation="@*android:anim/progress_indeterminate_horizontal_rect2" />
+ android:animation="@anim/progress_indeterminate_horizontal_rect2_copy" />
<target
android:name="rect1_grp"
- android:animation="@*android:anim/progress_indeterminate_horizontal_rect1" />
+ android:animation="@anim/progress_indeterminate_horizontal_rect1_copy" />
</animated-vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_scalex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_scalex_copy.xml
new file mode 100644
index 0000000..d808f4f
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_scalex_copy.xml
@@ -0,0 +1,18 @@
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect1_scalex in frameworks/base/core/res -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0 0 L 0.3665 0 C 0.47252618112021,0.062409910275 0.61541608570164,0.5 0.68325,0.5 C 0.75475061236836,0.5 0.75725829093844,0.814510098964 1.0,1.0" />
diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_translatex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_translatex_copy.xml
new file mode 100644
index 0000000..e78d5e7
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect1_translatex_copy.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect1_translatex in frameworks/base/core/res -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 L 0.2 0 C 0.3958333333336,0.0 0.474845090492,0.206797621729 0.5916666666664,0.417082932942 C 0.7151610251224,0.639379624869 0.81625,0.974556908664 1.0,1.0 " />
diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_scalex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_scalex_copy.xml
new file mode 100644
index 0000000..43c4124
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_scalex_copy.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect2_scalex in frameworks/base/core/res -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0,0 C 0.06834272400867,0.01992566661414 0.19220331656133,0.15855429260523 0.33333333333333,0.34926160892842 C 0.38410433133433,0.41477913453861 0.54945792615267,0.68136029463551 0.66666666666667,0.68279962777002 C 0.752586273196,0.68179620963216 0.737253971954,0.878896194318 1,1" />
diff --git a/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_translatex_copy.xml b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_translatex_copy.xml
new file mode 100644
index 0000000..615d6d6
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/interpolator/progress_indeterminate_horizontal_rect2_translatex_copy.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect2_translatex in frameworks/base/core/res -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 C 0.0375,0.0 0.128764607715,0.0895380946618 0.25,0.218553507947 C 0.322410320025,0.295610602487 0.436666666667,0.417591408114 0.483333333333,0.489826169306 C 0.69,0.80972296795 0.793333333333,0.950016125212 1.0,1.0 " />
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index 1a47afc..f532caa 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -42,6 +42,7 @@
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:minWidth="56dp"
+ android:minHeight="48dp"
android:orientation="horizontal"
android:clipToPadding="false"
android:paddingTop="4dp"
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 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/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md
new file mode 100644
index 0000000..5e4f6c7
--- /dev/null
+++ b/packages/SystemUI/docs/clock-plugins.md
@@ -0,0 +1,92 @@
+# Clock Plugins
+
+## Introduction
+
+The clock appearing on the lock screen and always on display (AOD) can be
+customized via the ClockPlugin plugin interface.
+
+## System Health
+
+Clocks are high risk for battery consumption and screen burn-in because they
+modify the UI of AOD.
+
+To reduce battery consumption, it is recommended to
+target a maximum on-pixel-ratio (OPR) of 5%. Clocks that are composed of
+large blocks of color that cause the OPR to exceed 5% should be avoided.
+
+To prevent screen burn-in, clocks should not be composed of large solid
+blocks of color, and the clock should be moved around the screen to
+distribute the on pixels across a large number of pixels. Software
+burn-in testing is a good starting point to assess the pixel shifting
+(clock movement) scheme and shape of the clock.
+
+### Software Burn-In Test
+
+The goal is to look for bright spots in the luminosity average over a period of
+time. It is difficult to define a threshold where burn-in will occur. It is,
+therefore, recommended to compare against an element on AOD that is known not
+to cause problems.
+
+For clock face that contain color, it is recommended to use an all white
+version of the face. Since white has the highest luminosity, this version of
+the clock face represents the worst case scenario.
+
+To start, generate a sequence of screenshots for each minute over a 12 hr interval.
+
+```
+serial = '84TY004MS' # serial number for the device
+count = 1
+t = datetime.datetime(2019, 1, 1)
+stop = t + datetime.timedelta(hours=12)
+if not os.path.exists(OUTPUT_FOLDER):
+ raise RuntimeError('output folder "%s" does not exist' % OUTPUT_FOLDER)
+while t <= stop:
+ os.system("adb -s %s shell 'date %s ; am broadcast -a android.intent.action.TIME_SET'" % (serial, t.strftime('%m%d%H%M%Y.%S')))
+ os.system('adb -s %s shell screencap -p > %s/screencap_%06d.png' % (serial, OUTPUT_FOLDER, count))
+ t += datetime.timedelta(minutes=1)
+ count += 1
+```
+
+Average the luminosity of the screenshots.
+
+```
+#!python
+import numpy
+import scipy.ndimage
+from imageio import imread, imwrite
+import matplotlib.pylab as plt
+import os
+import os.path
+
+def images(path):
+ return [os.path.join(path, name) for name in os.listdir(path) if name.endswith('.png')]
+
+def average(images):
+ AVG = None
+ for name in images:
+ IM = scipy.ndimage.imread(name, mode='L')
+ A = numpy.array(IM, dtype=numpy.double)
+ if AVG is None:
+ AVG = A
+ else:
+ AVG += A
+ AVG /= len(images)
+ return numpy.array(AVG, dtype=numpy.uint8)
+
+def main(path):
+ ims = images(path)
+ if len(ims) == 0:
+ raise ValueError("folder '%s' doesn't contain any png files" % path)
+ AVG = average(ims)
+ imwrite('average.png', AVG)
+ plt.imshow(AVG)
+ plt.show()
+
+if __name__=='__main__':
+ import sys
+ main(sys.argv[1])
+```
+
+Look for bright spots in the luminosity average. If bright spots are found,
+action should be taken to change the shape of the clock face or increase the
+amount of pixel shifting.
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png
index 83d714b..4a0972f 100644
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
index 8d0e6ed..a7d5a0d 100644
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
index 1ac0113..812dc9d 100644
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
Binary files differ
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/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/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 79e2dfb..83fad66 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -34,7 +34,7 @@
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="start"
+ android:layout_gravity="start|center_vertical"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status"
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 37c6d9f..5a33f82 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -22,17 +22,6 @@
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end">
- <TextView
- android:id="@+id/long_press_tooltip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:alpha="0"
- android:text="@string/quick_settings_header_onboarding_text"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"
- android:textColor="?android:attr/textColorSecondary"
- android:visibility="invisible" />
-
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
@@ -45,8 +34,7 @@
android:layout_height="match_parent"
android:layout_gravity="start|center_vertical"
android:layout_weight="1"
- android:gravity="center_vertical"
- android:alpha="0" >
+ android:gravity="center_vertical" >
<LinearLayout
android:id = "@+id/alarm_container"
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 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/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..5e84549 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>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0b0822c..a2039d0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -659,6 +659,9 @@
<!-- Accessibility text describing the presence of active location requests by one or more apps -->
<string name="accessibility_location_active">Location requests active</string>
+ <!-- Accessibility text describing sensors off active. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_sensors_off_active">Sensors off active</string>
+
<!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_clear_all">Clear all notifications.</string>
@@ -1648,17 +1651,29 @@
<!-- Notification Inline controls: continue receiving notifications prompt, app level -->
<string name="inline_keep_showing_app">Keep showing notifications from this app?</string>
- <!-- Hint text for block button in the interruptiveness settings [CHAR_LIMIT=NONE]-->
- <string name="hint_text_block">Blocked notifications do not appear anywhere or play a sound. You can unblock notifications in settings.</string>
+ <!-- [CHAR LIMIT=100] Notification Importance title -->
+ <string name="notification_silence_title">Gentle</string>
- <!-- 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/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 39da194..9dd5bb4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -19,6 +19,9 @@
import android.graphics.HardwareRenderer;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Message;
import android.view.Surface;
import android.view.View;
import android.view.ViewRootImpl;
@@ -33,8 +36,15 @@
*/
public class SyncRtSurfaceTransactionApplierCompat {
+ private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
+
private final Surface mTargetSurface;
private final ViewRootImpl mTargetViewRootImpl;
+ private final Handler mApplyHandler;
+
+ private int mSequenceNumber = 0;
+ private int mPendingSequenceNumber = 0;
+ private Runnable mAfterApplyCallback;
/**
* @param targetView The view in the surface that acts as synchronization anchor.
@@ -42,6 +52,26 @@
public SyncRtSurfaceTransactionApplierCompat(View targetView) {
mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null;
+
+ mApplyHandler = new Handler(new Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) {
+ onApplyMessage(msg.arg1);
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ private void onApplyMessage(int seqNo) {
+ mSequenceNumber = seqNo;
+ if (mSequenceNumber == mPendingSequenceNumber && mAfterApplyCallback != null) {
+ Runnable r = mAfterApplyCallback;
+ mAfterApplyCallback = null;
+ r.run();
+ }
}
/**
@@ -54,10 +84,15 @@
if (mTargetViewRootImpl == null || mTargetViewRootImpl.getView() == null) {
return;
}
+
+ mPendingSequenceNumber++;
+ final int toApplySeqNo = mPendingSequenceNumber;
mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
if (mTargetSurface == null || !mTargetSurface.isValid()) {
+ Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
+ .sendToTarget();
return;
}
TransactionCompat t = new TransactionCompat();
@@ -70,6 +105,7 @@
}
t.setEarlyWakeup();
t.apply();
+ mApplyHandler.sendEmptyMessage(toApplySeqNo);
}
});
@@ -77,6 +113,28 @@
mTargetViewRootImpl.getView().invalidate();
}
+ /**
+ * Calls the runnable when any pending apply calls have completed
+ */
+ public void addAfterApplyCallback(final Runnable afterApplyCallback) {
+ if (mSequenceNumber == mPendingSequenceNumber) {
+ afterApplyCallback.run();
+ } else {
+ if (mAfterApplyCallback == null) {
+ mAfterApplyCallback = afterApplyCallback;
+ } else {
+ final Runnable oldCallback = mAfterApplyCallback;
+ mAfterApplyCallback = new Runnable() {
+ @Override
+ public void run() {
+ afterApplyCallback.run();
+ oldCallback.run();
+ }
+ };
+ }
+ }
+ }
+
public static void applyParams(TransactionCompat t,
SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
t.setMatrix(params.surface, params.matrix);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 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/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index 922c65e..b3fc69e 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -59,7 +59,10 @@
} else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
if (DEBUG) Log.d(TAG, "Unregister " + uri);
- getAndRemoveRelay(uri).unregister(mContext);
+ BroadcastRelay relay = getAndRemoveRelay(uri);
+ if (relay != null) {
+ relay.unregister(mContext);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/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/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/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
index f19445c..a1a7566 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
@@ -132,15 +132,18 @@
}
@Override
+ protected void onFinishInflate() {
+ setSelected(true);
+ }
+
+ @Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
// Only show marquee when visible
if (visibility == VISIBLE) {
setEllipsize(TextUtils.TruncateAt.MARQUEE);
- setSelected(true);
} else {
setEllipsize(TextUtils.TruncateAt.END);
- setSelected(false);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 995dc66..4b55137 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -181,6 +181,7 @@
private static class HeaderTileLayout extends TileLayout {
private boolean mListening;
+ private Rect mClippingBounds = new Rect();
public HeaderTileLayout(Context context) {
super(context);
@@ -219,8 +220,8 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// We only care about clipping on the right side
- Rect bounds = new Rect(0, 0, r - l, 10000);
- setClipBounds(bounds);
+ mClippingBounds.set(0, 0, r - l, 10000);
+ setClipBounds(mClippingBounds);
calculateColumns();
@@ -252,9 +253,9 @@
}
final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
- final int leftoverWithespace = availableWidth - maxTiles * mCellWidth;
+ final int leftoverWhitespace = availableWidth - maxTiles * mCellWidth;
final int smallestHorizontalMarginNeeded;
- smallestHorizontalMarginNeeded = leftoverWithespace / Math.max(1, maxTiles - 1);
+ smallestHorizontalMarginNeeded = leftoverWhitespace / Math.max(1, maxTiles - 1);
if (smallestHorizontalMarginNeeded > 0){
mCellMarginHorizontal = smallestHorizontalMarginNeeded;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2e3065a..346ffa2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -18,8 +18,6 @@
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
@@ -39,7 +37,6 @@
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Pair;
import android.util.StatsLog;
import android.view.DisplayCutout;
@@ -56,7 +53,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -123,10 +119,6 @@
private View mSystemIconsView;
private View mQuickQsStatusIcons;
private View mHeaderTextContainerView;
- /** View containing the next alarm and ringer mode info. */
- private View mStatusContainer;
- /** Tooltip for educating users that they can long press on icons to see more details. */
- private View mLongPressTooltipView;
private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
private AlarmManager.AlarmClockInfo mNextAlarm;
@@ -146,8 +138,6 @@
private BatteryMeterView mBatteryRemainingIcon;
private PrivacyItemController mPrivacyItemController;
- /** Counts how many times the long press tooltip has been shown to the user. */
- private int mShownCount;
private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
@Override
@@ -159,11 +149,6 @@
private boolean mHasTopCutout = false;
private boolean mPrivacyChipLogged = false;
- /**
- * Runnable for automatically fading out the long press tooltip (as if it were animating away).
- */
- private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
-
private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
@Override
public void privacyChanged(List<PrivacyItem> privacyItems) {
@@ -183,7 +168,6 @@
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
mPrivacyItemController = privacyItemController;
- mShownCount = getStoredShownCount();
}
@Override
@@ -199,10 +183,8 @@
iconContainer.setShouldRestrictIcons(false);
mIconManager = new TintedIconManager(iconContainer);
- // Views corresponding to the header info section (e.g. tooltip and next alarm).
+ // Views corresponding to the header info section (e.g. ringer and next alarm).
mHeaderTextContainerView = findViewById(R.id.header_text_container);
- mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
- mStatusContainer = findViewById(R.id.status_container);
mStatusSeparator = findViewById(R.id.status_separator);
mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
mNextAlarmTextView = findViewById(R.id.next_alarm_text);
@@ -267,7 +249,6 @@
boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
: View.GONE);
- updateTooltipShow();
}
}
@@ -351,8 +332,6 @@
mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
}
-
-
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
@@ -457,21 +436,6 @@
mPrivacyChip.setExpanded(expansionFraction > 0.5);
mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
}
-
- // Check the original expansion fraction - we don't want to show the tooltip until the
- // panel is pulled all the way out.
- if (expansionFraction == 1f) {
- // QS is fully expanded, bring in the tooltip.
- showLongPressTooltip();
- }
- }
-
- /** Returns the latest stored tooltip shown count from SharedPreferences. */
- private int getStoredShownCount() {
- return Prefs.getInt(
- mContext,
- Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
- TOOLTIP_NOT_YET_SHOWN_COUNT);
}
public void disable(int state1, int state2, boolean animate) {
@@ -592,109 +556,6 @@
updateStatusText();
}
- private void updateTooltipShow() {
- if (hasStatusText()) {
- hideLongPressTooltip(true /* shouldShowStatusText */);
- } else {
- hideStatusText();
- }
- updateHeaderTextContainerAlphaAnimator();
- }
-
- private boolean hasStatusText() {
- return mNextAlarmTextView.getVisibility() == View.VISIBLE
- || mRingerModeTextView.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
- * the space).
- */
- public void showLongPressTooltip() {
- // If we have status text to show, don't bother fading in the tooltip.
- if (hasStatusText()) {
- return;
- }
-
- if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
- mLongPressTooltipView.animate().cancel();
- mLongPressTooltipView.setVisibility(View.VISIBLE);
- mLongPressTooltipView.animate()
- .alpha(1f)
- .setDuration(FADE_ANIMATION_DURATION_MS)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHandler.postDelayed(
- mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS);
- }
- })
- .start();
-
- // Increment and drop the shown count in prefs for the next time we're deciding to
- // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
- // in prefs (say, from a long press).
- if (getStoredShownCount() <= mShownCount) {
- Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
- }
- }
- }
-
- /**
- * Fades out the long press tooltip if it's partially visible - short circuits any running
- * animation. Additionally has the ability to fade in the status info text.
- *
- * @param shouldShowStatusText whether we should fade in the status text
- */
- private void hideLongPressTooltip(boolean shouldShowStatusText) {
- mLongPressTooltipView.animate().cancel();
- if (mLongPressTooltipView.getVisibility() == View.VISIBLE
- && mLongPressTooltipView.getAlpha() != 0f) {
- mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable);
- mLongPressTooltipView.animate()
- .alpha(0f)
- .setDuration(FADE_ANIMATION_DURATION_MS)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip");
- mLongPressTooltipView.setVisibility(View.INVISIBLE);
-
- if (shouldShowStatusText) {
- showStatus();
- }
- }
- })
- .start();
- } else {
- mLongPressTooltipView.setVisibility(View.INVISIBLE);
- if (shouldShowStatusText) {
- showStatus();
- }
- }
- }
-
- /**
- * Fades in the updated status text. Note that if there's already a status showing, this will
- * immediately fade it out and fade in the updated status.
- */
- private void showStatus() {
- mStatusContainer.setAlpha(0f);
-
- mStatusContainer.animate()
- .alpha(1f)
- .setDuration(FADE_ANIMATION_DURATION_MS)
- .start();
- }
-
- /** Fades out the status text. */
- private void hideStatusText() {
- mStatusContainer.animate()
- .alpha(0f)
- .setDuration(FADE_ANIMATION_DURATION_MS)
- .start();
- }
-
public void updateEverything() {
post(() -> setClickable(!mExpanded));
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index b2302cc..898f64b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -354,17 +354,22 @@
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mHandler.removeCallbacks(mDeferredConnectionCallback);
- mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
mConnectionBackoffAttempts = 0;
- mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
- // Listen for launcher's death
+ mHandler.removeCallbacks(mDeferredConnectionCallback);
try {
service.linkToDeath(mOverviewServiceDeathRcpt, 0);
} catch (RemoteException e) {
+ // Failed to link to death (process may have died between binding and connecting),
+ // just unbind the service for now and retry again
Log.e(TAG_OPS, "Lost connection to launcher service", e);
+ disconnectFromLauncherService();
+ retryConnectionWithBackoff();
+ return;
}
+ mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
+ mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
+
Bundle params = new Bundle();
params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
@@ -550,7 +555,6 @@
mHandler.post(()-> {
StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
if (bar != null) {
- System.out.println("MERONG dispatchNotificationPanelTouchEvent");
mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
mStatusBarGestureDownEvent.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 6101593..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}.
@@ -693,6 +710,9 @@
@Override
public void onKeyguardBouncerChanged(boolean bouncer) {
+ if (mLockIcon == null) {
+ return;
+ }
mLockIcon.setBouncerVisible(bouncer);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 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/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 058cb2d..ebda585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1193,6 +1193,20 @@
}
}
+ /**
+ * Returns best effort count of visible notifications.
+ */
+ public int getVisibleNotificationCount() {
+ int count = 0;
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
+ count++;
+ }
+ }
+ return count;
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean isCurrentlyAnimating() {
return mStateAnimator.isRunning();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 0171d7f..26e0a70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -59,6 +59,16 @@
private int mClockPreferredY;
/**
+ * Whether or not there is a custom clock face on keyguard.
+ */
+ private boolean mHasCustomClock;
+
+ /**
+ * Whether or not the NSSL contains any visible notifications.
+ */
+ private boolean mHasVisibleNotifs;
+
+ /**
* Height of notification stack: Sum of height of each notification.
*/
private int mNotificationStackHeight;
@@ -117,7 +127,7 @@
public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY,
- float dark, float emptyDragAmount) {
+ boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount) {
mMinTopMargin = minTopMargin + mContainerTopPadding;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
@@ -125,6 +135,8 @@
mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight;
mClockPreferredY = clockPreferredY;
+ mHasCustomClock = hasCustomClock;
+ mHasVisibleNotifs = hasVisibleNotifs;
mDarkAmount = dark;
mEmptyDragAmount = emptyDragAmount;
}
@@ -179,6 +191,9 @@
clockYDark = MathUtils.max(0, clockYDark);
float clockYRegular = getExpandedClockPosition();
+ if (mHasCustomClock && !mHasVisibleNotifs) {
+ clockYRegular = clockYDark;
+ }
float clockYBouncer = -mKeyguardStatusHeight;
// Move clock up while collapsing the shade
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 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 6c1a4fa..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;
}
}
@@ -715,7 +715,10 @@
}
mNavigationBarView.onNavigationButtonLongPress(v);
mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);
- mAssistManager.startAssist(new Bundle() /* args */);
+ Bundle args = new Bundle();
+ args.putInt(
+ AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS);
+ mAssistManager.startAssist(args);
mStatusBar.awakenDreams();
if (mNavigationBarView != null) {
@@ -900,6 +903,17 @@
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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2207e04..c39a494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -637,6 +637,8 @@
totalHeight,
mKeyguardStatusView.getHeight(),
clockPreferredY,
+ hasCustomClock(),
+ mNotificationStackScroller.getVisibleNotificationCount() != 0,
mInterpolatedDarkAmount,
mEmptyDragAmount);
mClockPositionAlgorithm.run(mClockPositionResult);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 183fdb4..d5706e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -238,7 +238,8 @@
mIconController.setIconVisibility(mSlotLocation, false);
// sensors off
- mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, null);
+ mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off,
+ mContext.getString(R.string.accessibility_sensors_off_active));
mIconController.setIconVisibility(mSlotSensorsOff,
mSensorPrivacyController.isSensorPrivacyEnabled());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3f33ba6..f08d7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -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/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/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
index c6e85c3..19e1a5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -95,6 +95,22 @@
}
@Test
+ public void testUnregisterWithoutRegister() {
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ relayHandler.handleIntent(intent);
+ // No crash
+ }
+
+ @Test
public void testRelay() {
Receiver.sReceiver = mock(BroadcastReceiver.class);
Uri testUri = new Uri.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 80c51cf..375b6e5 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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 9354648..f191cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -41,6 +41,8 @@
private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
+ private static final boolean HAS_CUSTOM_CLOCK = false;
+ private static final boolean HAS_VISIBLE_NOTIFS = false;
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private KeyguardClockPositionAlgorithm.Result mClockPosition;
@@ -48,11 +50,18 @@
private float mPanelExpansion;
private int mKeyguardStatusHeight;
private float mDark;
+ private int mPreferredClockY;
+ private boolean mHasCustomClock;
+ private boolean mHasVisibleNotifs;
@Before
public void setUp() {
mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
mClockPosition = new KeyguardClockPositionAlgorithm.Result();
+
+ mPreferredClockY = PREFERRED_CLOCK_Y;
+ mHasCustomClock = HAS_CUSTOM_CLOCK;
+ mHasVisibleNotifs = HAS_VISIBLE_NOTIFS;
}
@Test
@@ -293,6 +302,71 @@
assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
}
+ @Test
+ public void preferredCustomClockPositionNoNotifications() {
+ // GIVEN on the lock screen with a custom clock and no visible notifications
+ givenLockScreen();
+ mPreferredClockY = 100;
+ mHasCustomClock = true;
+ mHasVisibleNotifs = false;
+ // AND given empty height for clock and stack scroller
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the clock Y position is the preferred Y position.
+ assertThat(mClockPosition.clockY).isEqualTo(100);
+ }
+
+ @Test
+ public void preferredDefaultClockPositionNoNotifications() {
+ // GIVEN on the lock screen with a custom clock and no visible notifications
+ givenLockScreen();
+ mPreferredClockY = 100;
+ mHasCustomClock = false;
+ mHasVisibleNotifs = false;
+ // AND given empty height for clock and stack scroller
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2) and not
+ // preferred.
+ assertThat(mClockPosition.clockY).isEqualTo(1000);
+ }
+
+ @Test
+ public void preferredCustomClockPositionWithVisibleNotifications() {
+ // GIVEN on the lock screen with a custom clock and visible notifications
+ givenLockScreen();
+ mPreferredClockY = 100;
+ mHasCustomClock = true;
+ mHasVisibleNotifs = true;
+ // AND given empty height for clock and stack scroller
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.clockY).isEqualTo(1000);
+ }
+
+ @Test
+ public void preferredCustomClockPositionWithVisibleNotificationsOnAod() {
+ // GIVEN on the lock screen with a custom clock and visible notifications
+ givenAOD();
+ mPreferredClockY = 100;
+ mHasCustomClock = true;
+ mHasVisibleNotifs = true;
+ // AND given empty height for clock and stack scroller
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the clock Y position is the preferred Y position.
+ assertThat(mClockPosition.clockY).isEqualTo(100);
+ }
+
private void givenAOD() {
mPanelExpansion = 1.f;
mDark = 1.f;
@@ -305,8 +379,8 @@
private void positionClock() {
mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
- mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, PREFERRED_CLOCK_Y, mDark,
- ZERO_DRAG);
+ mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY,
+ mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml
index c420ab6..a99f705 100644
--- a/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml
+++ b/packages/overlays/AccentColorCinnamonOverlay/res/values/colors_device_defaults.xml
@@ -18,5 +18,5 @@
-->
<resources>
<color name="accent_device_default_light">#AF6050</color>
- <color name="accent_device_default_dark">#9D6962</color>
+ <color name="accent_device_default_dark">#C3A6A2</color>
</resources>
diff --git a/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml
index 6aec805..449639b 100644
--- a/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml
+++ b/packages/overlays/AccentColorOceanOverlay/res/values/colors_device_defaults.xml
@@ -18,5 +18,5 @@
-->
<resources>
<color name="accent_device_default_light">#0C80A7</color>
- <color name="accent_device_default_dark">#347D98</color>
+ <color name="accent_device_default_dark">#28BDD7</color>
</resources>
diff --git a/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml
index 049f8b8..47079a8 100644
--- a/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml
+++ b/packages/overlays/AccentColorOrchidOverlay/res/values/colors_device_defaults.xml
@@ -18,5 +18,5 @@
-->
<resources>
<color name="accent_device_default_light">#C42CC9</color>
- <color name="accent_device_default_dark">#C42CC9</color>
+ <color name="accent_device_default_dark">#E68AED</color>
</resources>
diff --git a/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml b/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml
index b6f757c..f147aeb 100644
--- a/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml
+++ b/packages/overlays/AccentColorSpaceOverlay/res/values/colors_device_defaults.xml
@@ -18,5 +18,5 @@
-->
<resources>
<color name="accent_device_default_light">#47618A</color>
- <color name="accent_device_default_dark">#5D7A92</color>
+ <color name="accent_device_default_dark">#99ACCC</color>
</resources>
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/config.xml b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
index 7b65555..2016ece 100644
--- a/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/config.xml
@@ -23,6 +23,8 @@
<bool name="config_useRoundIcon">false</bool>
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">0dp</dimen>
-
+ <!-- Corner radius for bottom sheet system dialogs -->
+ <dimen name="config_bottomDialogCornerRadius">0dp</dimen>
+
</resources>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3babb6d..21c6035 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7182,6 +7182,30 @@
// OS: Q
QS_UI_MODE_NIGHT = 1706;
+ // OPEN: Settings > Pick SIM dialog
+ // CATEGORY: SETTINGS
+ // OS: Q
+ DIALOG_SIM_LIST = 1707;
+
+ // OPEN: Settings > Pick SIM (that supports calling) dialog
+ // CATEGORY: SETTINGS
+ // OS: Q
+ DIALOG_CALL_SIM_LIST = 1708;
+
+ // OPEN: Settings > Pick preferred SIM dialog
+ // CATEGORY: SETTINGS
+ // OS: Q
+ DIALOG_PREFERRED_SIM_PICKER = 1709;
+
+ // ACTION: Share a Wi-Fi network by generating a QR code
+ ACTION_SETTINGS_SHARE_WIFI_QR_CODE = 1710;
+
+ // ACTION: Connect to a Wi-Fi network by scanning a QR code
+ ACTION_SETTINGS_ENROLL_WIFI_QR_CODE = 1711;
+
+ // ACTION: Share Wi-Fi hotspot by generating a QR code
+ ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 834987c..ea044cf 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -2098,6 +2098,9 @@
// Firmware alert code. Only valid when the stats was triggered by a firmware
// alert, otherwise -1.
optional int32 firmware_alert_code = 4 [default = -1];
+
+ // Absolute milliseconds from device boot when these stats were sampled
+ optional int64 time_stamp_ms = 5;
}
message DeviceMobilityStatePnoScanStats {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2c9e640..05b937a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2504,7 +2504,7 @@
* registered.
*/
@Override
- public int getAccessibilityWindowId(IBinder windowToken) {
+ public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
synchronized (mLock) {
if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
throw new SecurityException("Only SYSTEM can call getAccessibilityWindowId");
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 7f411d8..a2d3d4c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -781,36 +781,46 @@
@GuardedBy("mGlobalWhitelistStateLock")
public ContentCaptureOptions getOptions(@UserIdInt int userId,
@NonNull String packageName) {
+ boolean packageWhitelisted;
+ ArraySet<ComponentName> whitelistedComponents = null;
synchronized (mGlobalWhitelistStateLock) {
- if (!isWhitelisted(userId, packageName)) {
- if (packageName.equals(mServicePackages.get(userId))) {
+ packageWhitelisted = isWhitelisted(userId, packageName);
+ if (!packageWhitelisted) {
+ // Full package is not whitelisted: check individual components first
+ whitelistedComponents = getWhitelistedComponents(userId, packageName);
+ if (whitelistedComponents == null
+ && packageName.equals(mServicePackages.get(userId))) {
+ // No components whitelisted either, but let it go because it's the
+ // service's own package
if (verbose) Slog.v(mTag, "getOptionsForPackage() lite for " + packageName);
return new ContentCaptureOptions(mDevCfgLoggingLevel);
}
- if (verbose) {
- Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted");
- }
+ }
+ } // synchronized
+
+ // Restrict what temporary services can whitelist
+ if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) {
+ if (!packageName.equals(mServicePackages.get(userId))) {
+ Slog.w(mTag, "Ignoring package " + packageName + " while using temporary "
+ + "service " + mServicePackages.get(userId));
return null;
}
-
- final ArraySet<ComponentName> whitelistedComponents =
- getWhitelistedComponents(userId, packageName);
- if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) {
- if (!packageName.equals(mServicePackages.get(userId))) {
- Slog.w(mTag, "Ignoring package " + packageName
- + " while using temporary service " + mServicePackages.get(userId));
- return null;
- }
- }
- final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel,
- mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs,
- mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize,
- whitelistedComponents);
- if (verbose) {
- Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options);
- }
- return options;
}
+
+ if (!packageWhitelisted && whitelistedComponents == null) {
+ // No can do!
+ if (verbose) {
+ Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted");
+ }
+ return null;
+ }
+
+ final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel,
+ mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs,
+ mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize,
+ whitelistedComponents);
+ if (verbose) Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options);
+ return options;
}
@Override
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 5a42e78..a7921b5 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -35,13 +35,11 @@
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.content.ComponentName;
-import android.content.ContentCaptureOptions;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
@@ -60,7 +58,6 @@
import android.view.contentcapture.DataRemovalRequest;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.WhitelistHelper;
import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
@@ -97,12 +94,6 @@
new ContentCaptureServiceRemoteCallback();
/**
- * List of packages that are whitelisted to be content captured.
- */
- @GuardedBy("mLock")
- private final WhitelistHelper mWhitelistHelper = new WhitelistHelper();
-
- /**
* List of conditions keyed by package.
*/
@GuardedBy("mLock")
@@ -131,6 +122,7 @@
* Updates the reference to the remote service.
*/
private void updateRemoteServiceLocked(boolean disabled) {
+ if (mMaster.verbose) Slog.v(TAG, "updateRemoteService(disabled=" + disabled + ")");
if (mRemoteService != null) {
if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service");
mRemoteService.destroy();
@@ -247,7 +239,8 @@
final int displayId = activityPresentationInfo.displayId;
final ComponentName componentName = activityPresentationInfo.componentName;
final boolean whiteListed = mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId,
- componentName);
+ componentName) || mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId,
+ componentName.getPackageName());
final ComponentName serviceComponentName = getServiceComponentName();
final boolean enabled = isEnabledLocked();
if (mMaster.mRequestsHistory != null) {
@@ -462,40 +455,6 @@
@GuardedBy("mLock")
@Nullable
- ContentCaptureOptions getOptionsForPackageLocked(@NonNull String packageName) {
- if (!mWhitelistHelper.isWhitelisted(packageName)) {
- if (packageName.equals(getServicePackageName())) {
- if (mMaster.verbose) Slog.v(mTag, "getOptionsForPackage() lite for " + packageName);
- return new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel);
- }
- if (mMaster.verbose) {
- Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted");
- }
- return null;
- }
-
- final ArraySet<ComponentName> whitelistedComponents = mWhitelistHelper
- .getWhitelistedComponents(packageName);
- if (Build.IS_USER && isTemporaryServiceSetLocked()) {
- final String servicePackageName = getServicePackageName();
- if (!packageName.equals(servicePackageName)) {
- Slog.w(mTag, "Ignoring package " + packageName
- + " while using temporary service " + servicePackageName);
- return null;
- }
- }
- final ContentCaptureOptions options = new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel,
- mMaster.mDevCfgMaxBufferSize, mMaster.mDevCfgIdleFlushingFrequencyMs,
- mMaster.mDevCfgTextChangeFlushingFrequencyMs, mMaster.mDevCfgLogHistorySize,
- whitelistedComponents);
- if (mMaster.verbose) {
- Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options);
- }
- return options;
- }
-
- @GuardedBy("mLock")
- @Nullable
ArraySet<ContentCaptureCondition> getContentCaptureConditionsLocked(
@NonNull String packageName) {
return mConditionsByPkg.get(packageName);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 1bd367c..1fa62ce 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -3152,7 +3152,7 @@
void removeLocked(final int uid) {
if (uid == Process.SYSTEM_UID) {
- Slog.wtf(TAG, "removeLocked: Shouldn't for UID=" + uid);
+ // If a force-stop occurs for a system-uid package, ignore it.
return;
}
boolean didRemove = false;
@@ -3247,7 +3247,7 @@
// Only called for ephemeral apps
void removeForStoppedLocked(final int uid) {
if (uid == Process.SYSTEM_UID) {
- Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for UID=" + uid);
+ // If a force-stop occurs for a system-uid package, ignore it.
return;
}
boolean didRemove = false;
@@ -3291,7 +3291,7 @@
void removeUserLocked(int userHandle) {
if (userHandle == UserHandle.USER_SYSTEM) {
- Slog.wtf(TAG, "removeForStoppedLocked: Shouldn't for user=" + userHandle);
+ // If we're told we're removing the system user, ignore it.
return;
}
boolean didRemove = false;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e4c39cc..ee8bb05 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -47,6 +47,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.BroadcastOptions;
import android.app.NotificationManager;
@@ -632,7 +633,8 @@
* the first network for a given type changes, or if the default network
* changes.
*/
- private class LegacyTypeTracker {
+ @VisibleForTesting
+ static class LegacyTypeTracker {
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -658,10 +660,12 @@
* - dump is thread-safe with respect to concurrent add and remove calls.
*/
private final ArrayList<NetworkAgentInfo> mTypeLists[];
+ @NonNull
+ private final ConnectivityService mService;
- public LegacyTypeTracker() {
- mTypeLists = (ArrayList<NetworkAgentInfo>[])
- new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
+ LegacyTypeTracker(@NonNull ConnectivityService service) {
+ mService = service;
+ mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
}
public void addSupportedType(int type) {
@@ -710,10 +714,10 @@
}
// Send a broadcast if this is the first network of its type or if it's the default.
- final boolean isDefaultNetwork = isDefaultNetwork(nai);
+ final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
if ((list.size() == 1) || isDefaultNetwork) {
maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
- sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
+ mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
}
}
@@ -731,19 +735,18 @@
}
}
- final DetailedState state = DetailedState.DISCONNECTED;
-
if (wasFirstNetwork || wasDefault) {
- maybeLogBroadcast(nai, state, type, wasDefault);
- sendLegacyNetworkBroadcast(nai, state, type);
+ maybeLogBroadcast(nai, DetailedState.DISCONNECTED, type, wasDefault);
+ mService.sendLegacyNetworkBroadcast(nai, DetailedState.DISCONNECTED, type);
}
if (!list.isEmpty() && wasFirstNetwork) {
if (DBG) log("Other network available for type " + type +
", sending connected broadcast");
final NetworkAgentInfo replacement = list.get(0);
- maybeLogBroadcast(replacement, state, type, isDefaultNetwork(replacement));
- sendLegacyNetworkBroadcast(replacement, state, type);
+ maybeLogBroadcast(replacement, DetailedState.CONNECTED, type,
+ mService.isDefaultNetwork(replacement));
+ mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type);
}
}
@@ -758,7 +761,7 @@
// send out another legacy broadcast - currently only used for suspend/unsuspend
// toggle
public void update(NetworkAgentInfo nai) {
- final boolean isDefault = isDefaultNetwork(nai);
+ final boolean isDefault = mService.isDefaultNetwork(nai);
final DetailedState state = nai.networkInfo.getDetailedState();
for (int type = 0; type < mTypeLists.length; type++) {
final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
@@ -766,7 +769,7 @@
final boolean isFirst = contains && (nai == list.get(0));
if (isFirst || contains && isDefault) {
maybeLogBroadcast(nai, state, type, isDefault);
- sendLegacyNetworkBroadcast(nai, state, type);
+ mService.sendLegacyNetworkBroadcast(nai, state, type);
}
}
}
@@ -802,7 +805,7 @@
pw.println();
}
}
- private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
+ private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this);
/**
* Helper class which parses out priority arguments and dumps sections according to their
@@ -2808,6 +2811,11 @@
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
mNai.network.netId));
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -5357,7 +5365,8 @@
}
}
- private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+ @VisibleForTesting
+ protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
return nai == getDefaultNetwork();
}
@@ -6565,7 +6574,8 @@
}
}
- private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
+ @VisibleForTesting
+ protected void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
// The NetworkInfo we actually send out has no bearing on the real
// state of affairs. For example, if the default connection is mobile,
// and a request for HIPRI has just gone away, we need to pretend that
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index 6b42b3d..5bd27c4 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -159,7 +159,13 @@
}
}
- /** Does the {@link /sys/class/extcon} directory exist */
+ /** Does the {@code /sys/class/extcon/<name>} directory exist */
+ public static boolean namedExtconDirExists(String name) {
+ File extconDir = new File("/sys/class/extcon/" + name);
+ return extconDir.exists() && extconDir.isDirectory();
+ }
+
+ /** Does the {@code /sys/class/extcon} directory exist */
public static boolean extconExists() {
File extconDir = new File("/sys/class/extcon");
return extconDir.exists() && extconDir.isDirectory();
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 3410d8d..44fc45e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -88,6 +88,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
@@ -3567,6 +3568,10 @@
return;
}
pw.println("Current Location Manager state:");
+ pw.print(" Current System Time: "
+ + TimeUtils.logTimeOfDay(System.currentTimeMillis()));
+ pw.println(", Current Elapsed Time: "
+ + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
pw.println(" Current user: " + mCurrentUserId + " " + Arrays.toString(
mCurrentUserProfiles));
pw.println(" Location mode: " + isLocationEnabled());
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index cc3b43a..c2b35c18 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -96,6 +96,7 @@
"media.extractor", // system/bin/mediaextractor
"media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
+ "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec
"com.android.bluetooth", // Bluetooth service
"/system/bin/statsd", // Stats daemon
};
@@ -108,6 +109,7 @@
"android.hardware.graphics.allocator@2.0::IAllocator",
"android.hardware.graphics.composer@2.1::IComposer",
"android.hardware.health@2.0::IHealth",
+ "android.hardware.media.c2@1.0::IComponentStore",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
"android.hardware.sensors@1.0::ISensors",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0271354..24f8fc2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3830,8 +3830,11 @@
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
-
synchronized(mAm) {
+ if (proc.isDebugging()) {
+ // The app's being debugged, ignore timeout.
+ return;
+ }
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0b60282..6da7f5f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7894,8 +7894,9 @@
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (!isDebuggable) {
- if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
- throw new SecurityException("Process not debuggable: " + app.packageName);
+ if (!app.isProfileableByShell()) {
+ throw new SecurityException("Process not debuggable, "
+ + "and not profileable by shell: " + app.packageName);
}
}
mProfileData.setProfileApp(processName);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 236797b..5255316 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -407,6 +407,7 @@
SynchronousResultReceiver wifiReceiver = null;
SynchronousResultReceiver bluetoothReceiver = null;
SynchronousResultReceiver modemReceiver = null;
+ boolean railUpdated = false;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
// We were asked to fetch WiFi data.
@@ -426,6 +427,10 @@
// Oh well.
}
}
+ synchronized (mStats) {
+ mStats.updateRailStatsLocked();
+ }
+ railUpdated = true;
}
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
@@ -447,6 +452,11 @@
modemReceiver = new SynchronousResultReceiver("telephony");
mTelephony.requestModemActivityInfo(modemReceiver);
}
+ if (!railUpdated) {
+ synchronized (mStats) {
+ mStats.updateRailStatsLocked();
+ }
+ }
}
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
@@ -477,10 +487,6 @@
mStats.updateRpmStatsLocked();
}
- if ((updateFlags & UPDATE_RAIL) != 0) {
- mStats.updateRailStatsLocked();
- }
-
if (bluetoothInfo != null) {
if (bluetoothInfo.isValid()) {
mStats.updateBluetoothStateLocked(bluetoothInfo);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1bcc4c8..a93f218 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3172,15 +3172,28 @@
@GuardedBy("mService")
void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+ boolean foundProcess = false;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
try {
+ for (int index = packages.length - 1; index >= 0 && !foundProcess; index--) {
+ if (packages[index].equals(r.info.packageName)) {
+ foundProcess = true;
+ }
+ }
r.thread.dispatchPackageBroadcast(cmd, packages);
} catch (RemoteException ex) {
}
}
}
+
+ if (!foundProcess) {
+ try {
+ AppGlobals.getPackageManager().notifyPackagesReplacedReceived(packages);
+ } catch (RemoteException ignored) {
+ }
+ }
}
/** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 49930c1..e891e6e 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1557,7 +1557,7 @@
mService.mBatteryStatsService.noteProcessAnr(processName, uid);
}
- if (isSilentAnr()) {
+ if (isSilentAnr() && !isDebugging()) {
kill("bg anr", true);
return;
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 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/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index f198464..d56cb79 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -46,26 +46,12 @@
],
"postsubmit": [
{
- "name": "CtsActivityManagerDeviceTestCases"
- },
- {
- "name": "CtsActivityManagerDeviceSdk25TestCases"
- },
- {
"name": "FrameworksServicesTests",
"options": [
{
"include-filter": "com.android.server.am."
}
]
- },
- {
- "name": "WmTests",
- "options": [
- {
- "include-filter": "com.android.server.am."
- }
- ]
}
]
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c5733322..05be100 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -43,7 +43,6 @@
import com.android.internal.annotations.GuardedBy;
-import java.util.ArrayList;
/** @hide */
/*package*/ final class AudioDeviceBroker {
@@ -376,24 +375,29 @@
mAudioService.postAccessoryPlugMediaUnmute(device);
}
- /*package*/ AudioService.VolumeStreamState getStreamState(int streamType) {
- return mAudioService.getStreamState(streamType);
+ /*package*/ int getVssVolumeForDevice(int streamType, int device) {
+ return mAudioService.getVssVolumeForDevice(streamType, device);
}
- /*package*/ ArrayList<AudioService.SetModeDeathHandler> getSetModeDeathHandlers() {
- return mAudioService.mSetModeDeathHandlers;
+ /*package*/ int getModeOwnerPid() {
+ return mAudioService.getModeOwnerPid();
}
/*package*/ int getDeviceForStream(int streamType) {
return mAudioService.getDeviceForStream(streamType);
}
- /*package*/ void setDeviceVolume(AudioService.VolumeStreamState streamState, int device) {
- mAudioService.setDeviceVolume(streamState, device);
+ /*package*/ void postApplyVolumeOnDevice(int streamType, int device, String caller) {
+ mAudioService.postApplyVolumeOnDevice(streamType, device, caller);
}
- /*packages*/ void observeDevicesForAllStreams() {
- mAudioService.observeDevicesForAllStreams();
+ /*package*/ void postSetVolumeIndexOnDevice(int streamType, int vssVolIndex, int device,
+ String caller) {
+ mAudioService.postSetVolumeIndexOnDevice(streamType, vssVolIndex, device, caller);
+ }
+
+ /*packages*/ void postObserveDevicesForAllStreams() {
+ mAudioService.postObserveDevicesForAllStreams();
}
/*package*/ boolean isInCommunication() {
@@ -767,7 +771,7 @@
case MSG_L_SCOCLIENT_DIED:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mBtHelper.scoClientDied(msg.arg1);
+ mBtHelper.scoClientDied(msg.obj);
}
}
break;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 91b51b4..f9dbdd5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -198,14 +198,10 @@
}
}
if (a2dpVolume != -1) {
- AudioService.VolumeStreamState streamState =
- mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC);
- // Convert index to internal representation in VolumeStreamState
- a2dpVolume = a2dpVolume * 10;
- streamState.setIndex(a2dpVolume, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- "onSetA2dpSinkConnectionState");
- mDeviceBroker.setDeviceVolume(
- streamState, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+ mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+ // convert index to internal representation in VolumeStreamState
+ a2dpVolume * 10,
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
}
makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
"onSetA2dpSinkConnectionState", a2dpCodec);
@@ -302,14 +298,11 @@
if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) {
// Device is connected
if (a2dpVolume != -1) {
- final AudioService.VolumeStreamState streamState =
- mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC);
- // Convert index to internal representation in VolumeStreamState
- a2dpVolume = a2dpVolume * 10;
- streamState.setIndex(a2dpVolume, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+ // convert index to internal representation in VolumeStreamState
+ a2dpVolume * 10,
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
"onBluetoothA2dpActiveDeviceChange");
- mDeviceBroker.setDeviceVolume(
- streamState, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
}
} else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
if (di.mDeviceCodecFormat != a2dpCodec) {
@@ -352,7 +345,7 @@
}
}
mRoutesObservers.finishBroadcast();
- mDeviceBroker.observeDevicesForAllStreams();
+ mDeviceBroker.postObserveDevicesForAllStreams();
}
private static final int DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG =
@@ -655,8 +648,6 @@
int a2dpCodec) {
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
- AudioService.VolumeStreamState streamState =
- mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC);
mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
@@ -727,8 +718,8 @@
@GuardedBy("mConnectedDevices")
private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
- final int hearingAidVolIndex = mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC)
- .getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
+ final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
+ AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, AudioSystem.STREAM_MUSIC);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
@@ -739,9 +730,8 @@
new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name,
address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
- mDeviceBroker.setDeviceVolume(
- mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC),
- AudioSystem.DEVICE_OUT_HEARING_AID);
+ mDeviceBroker.postApplyVolumeOnDevice(AudioSystem.STREAM_MUSIC,
+ AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
setCurrentAudioRouteNameIfPossible(name);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2d00f29..d3af8f07 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -258,6 +258,8 @@
private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23;
private static final int MSG_ENABLE_SURROUND_FORMATS = 24;
private static final int MSG_UPDATE_RINGER_MODE = 25;
+ private static final int MSG_SET_DEVICE_STREAM_VOLUME = 26;
+ private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -274,8 +276,8 @@
/** @see VolumeStreamState */
private VolumeStreamState[] mStreamStates;
- /*package*/ VolumeStreamState getStreamState(int stream) {
- return mStreamStates[stream];
+ /*package*/ int getVssVolumeForDevice(int stream, int device) {
+ return mStreamStates[stream].getIndex(device);
}
private SettingsObserver mSettingsObserver;
@@ -517,6 +519,10 @@
AudioSystem.DEVICE_OUT_AUX_LINE;
// Devices for which the volume is always max, no volume panel
int mFullVolumeDevices = 0;
+ // Devices for the which use the "absolute volume" concept (framework sends audio signal
+ // full scale, and volume control separately) and can be used for multiple use cases reflected
+ // by the audio mode (e.g. media playback in MODE_NORMAL, and phone calls in MODE_IN_CALL).
+ int mAbsVolumeMultiModeCaseDevices = AudioSystem.DEVICE_OUT_HEARING_AID;
private final boolean mMonitorRotation;
@@ -2048,6 +2054,49 @@
}
}
+ /**
+ * Manage an audio mode change for audio devices that use an "absolute volume" model,
+ * i.e. the framework sends the full scale signal, and the actual volume for the use case
+ * is communicated separately.
+ */
+ void updateAbsVolumeMultiModeDevices(int oldMode, int newMode) {
+ if (oldMode == newMode) {
+ return;
+ }
+ int streamType = AudioSystem.STREAM_MUSIC;
+ switch (newMode) {
+ case AudioSystem.MODE_IN_COMMUNICATION:
+ case AudioSystem.MODE_IN_CALL:
+ streamType = AudioSystem.STREAM_VOICE_CALL;
+ break;
+ case AudioSystem.MODE_NORMAL:
+ streamType = AudioSystem.STREAM_MUSIC;
+ break;
+ case AudioSystem.MODE_RINGTONE:
+ // not changing anything for ringtone
+ return;
+ case AudioSystem.MODE_CURRENT:
+ case AudioSystem.MODE_INVALID:
+ default:
+ // don't know what to do in this case, better bail
+ return;
+ }
+ final int device = AudioSystem.getDevicesForStream(streamType);
+ if ((device & mAbsVolumeMultiModeCaseDevices) == 0) {
+ return;
+ }
+
+ // handling of specific interfaces goes here:
+ if ((device & mAbsVolumeMultiModeCaseDevices) == AudioSystem.DEVICE_OUT_HEARING_AID) {
+ final int index = getStreamVolume(streamType);
+ mModeLogger.log(new AudioEventLogger.StringEvent("setMode to "
+ + AudioSystem.modeToString(newMode)
+ + " causes setting HEARING_AID volume to idx:" + index
+ + " stream:" + AudioSystem.streamToString(streamType)));
+ mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
+ }
+ }
+
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
String caller, int uid) {
if (DEBUG_VOL) {
@@ -2936,7 +2985,21 @@
}
- /*package*/ class SetModeDeathHandler implements IBinder.DeathRecipient {
+ /**
+ * Return the pid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ /*package*/ int getModeOwnerPid() {
+ int modeOwnerPid = 0;
+ try {
+ modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
+ } catch (Exception e) {
+ // nothing to do, modeOwnerPid is not modified
+ }
+ return modeOwnerPid;
+ }
+
+ private class SetModeDeathHandler implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
private int mPid;
private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
@@ -3023,9 +3086,9 @@
}
}
- // must be called synchronized on mSetModeLock
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
+ @GuardedBy("mDeviceBroker.mSetModeLock")
private int setModeInt(int mode, IBinder cb, int pid, String caller) {
if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
+ caller + ")"); }
@@ -3047,6 +3110,7 @@
break;
}
}
+ final int oldMode = mMode;
int status = AudioSystem.AUDIO_STATUS_OK;
int actualMode;
do {
@@ -3118,6 +3182,9 @@
setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller);
updateStreamVolumeAlias(true /*updateVolumes*/, caller);
+
+ // change of mode may require volume to be re-applied on some devices
+ updateAbsVolumeMultiModeDevices(oldMode, actualMode);
}
return newModeOwnerPid;
}
@@ -4096,8 +4163,14 @@
}
}
+ /*package*/ void postObserveDevicesForAllStreams() {
+ sendMsg(mAudioHandler,
+ MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS,
+ SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, null /*obj*/,
+ 0 /*delay*/);
+ }
- /*package*/ void observeDevicesForAllStreams() {
+ private void onObserveDevicesForAllStreams() {
observeDevicesForStreams(-1);
}
@@ -4270,7 +4343,7 @@
// 2 mSetModeLock
// 3 mSettingsLock
// 4 VolumeStreamState.class
- public class VolumeStreamState {
+ private class VolumeStreamState {
private final int mStreamType;
private int mIndexMin;
private int mIndexMax;
@@ -4740,6 +4813,74 @@
}
}
+ private static final class DeviceVolumeUpdate {
+ final int mStreamType;
+ final int mDevice;
+ final @NonNull String mCaller;
+ private static final int NO_NEW_INDEX = -2049;
+ private final int mVssVolIndex;
+
+ // Constructor with volume index, meant to cause this volume to be set and applied for the
+ // given stream type on the given device
+ DeviceVolumeUpdate(int streamType, int vssVolIndex, int device, @NonNull String caller) {
+ mStreamType = streamType;
+ mVssVolIndex = vssVolIndex;
+ mDevice = device;
+ mCaller = caller;
+ }
+
+ // Constructor with no volume index, meant to cause re-apply of volume for the given
+ // stream type on the given device
+ DeviceVolumeUpdate(int streamType, int device, @NonNull String caller) {
+ mStreamType = streamType;
+ mVssVolIndex = NO_NEW_INDEX;
+ mDevice = device;
+ mCaller = caller;
+ }
+
+ boolean hasVolumeIndex() {
+ return mVssVolIndex != NO_NEW_INDEX;
+ }
+
+ int getVolumeIndex() throws IllegalStateException {
+ Preconditions.checkState(mVssVolIndex != NO_NEW_INDEX);
+ return mVssVolIndex;
+ }
+ }
+
+ /*package*/ void postSetVolumeIndexOnDevice(int streamType, int vssVolIndex, int device,
+ String caller) {
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_STREAM_VOLUME,
+ SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/,
+ new DeviceVolumeUpdate(streamType, vssVolIndex, device, caller),
+ 0 /*delay*/);
+ }
+
+ /*package*/ void postApplyVolumeOnDevice(int streamType, int device, @NonNull String caller) {
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_STREAM_VOLUME,
+ SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/,
+ new DeviceVolumeUpdate(streamType, device, caller),
+ 0 /*delay*/);
+ }
+
+ private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) {
+ synchronized (VolumeStreamState.class) {
+ final VolumeStreamState streamState = mStreamStates[update.mStreamType];
+ if (update.hasVolumeIndex()) {
+ final int index = update.getVolumeIndex();
+ streamState.setIndex(index, update.mDevice, update.mCaller);
+ sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller + " dev:0x"
+ + Integer.toHexString(update.mDevice) + " volIdx:" + index));
+ } else {
+ sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller
+ + " update vol on dev:0x" + Integer.toHexString(update.mDevice)));
+ }
+ setDeviceVolume(streamState, update.mDevice);
+ }
+ }
+
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
@@ -5180,6 +5321,14 @@
case MSG_UPDATE_RINGER_MODE:
onUpdateRingerModeServiceInt();
break;
+
+ case MSG_SET_DEVICE_STREAM_VOLUME:
+ onSetVolumeIndexOnDevice((DeviceVolumeUpdate) msg.obj);
+ break;
+
+ case MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS:
+ onObserveDevicesForAllStreams();
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 332ff36..068c3d8 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -765,8 +765,7 @@
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
// currently controlled by the same client process.
- int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty()
- ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
+ final int modeOwnerPid = mDeviceBroker.getModeOwnerPid();
if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
+ modeOwnerPid + " != creatorPid " + mCreatorPid);
diff --git a/services/core/java/com/android/server/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/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 1820acf..ccd1db4 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
+import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
@@ -49,6 +50,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseSetArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -277,9 +279,9 @@
.append(", ")
.append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ")
.append("quotaCutoffTime=").append(quotaCutoffTimeElapsed).append(", ")
- .append("jobCountExpirationTime").append(jobCountExpirationTimeElapsed)
+ .append("jobCountExpirationTime=").append(jobCountExpirationTimeElapsed)
.append(", ")
- .append("jobCountInAllowedTime").append(jobCountInAllowedTime)
+ .append("jobCountInAllowedTime=").append(jobCountInAllowedTime)
.toString();
}
@@ -338,6 +340,9 @@
/** List of UIDs currently in the foreground. */
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+ /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
+ private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
+
/**
* List of jobs that started while the UID was in the TOP state. There will be no more than
* 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
@@ -421,6 +426,22 @@
}
};
+ private final BroadcastReceiver mPackageAddedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ return;
+ }
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ synchronized (mLock) {
+ mUidToPackageCache.remove(uid);
+ }
+ }
+ };
+
/**
* The rolling window size for each standby bucket. Within each window, an app will have 10
* minutes to run its jobs.
@@ -469,6 +490,9 @@
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
+
// Set up the app standby bucketing tracker
UsageStatsManagerInternal usageStats = LocalServices.getService(
UsageStatsManagerInternal.class);
@@ -502,10 +526,15 @@
@Override
public void prepareForExecutionLocked(JobStatus jobStatus) {
- if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+ if (DEBUG) {
+ Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+ }
final int uid = jobStatus.getSourceUid();
if (mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_TOP) {
+ if (DEBUG) {
+ Slog.d(TAG, jobStatus.toShortString() + " is top started job");
+ }
mTopStartedJobs.add(jobStatus);
// Top jobs won't count towards quota so there's no need to involve the Timer.
return;
@@ -518,7 +547,7 @@
timer = new Timer(uid, userId, packageName);
mPkgTimers.add(userId, packageName, timer);
}
- timer.startTrackingJob(jobStatus);
+ timer.startTrackingJobLocked(jobStatus);
}
@Override
@@ -645,7 +674,7 @@
if (timer != null) {
if (timer.isActive()) {
Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off.");
- timer.dropEverything();
+ timer.dropEverythingLocked();
}
mPkgTimers.delete(userId, packageName);
}
@@ -657,6 +686,7 @@
}
mExecutionStatsCache.delete(userId, packageName);
mForegroundUids.delete(uid);
+ mUidToPackageCache.remove(uid);
}
@Override
@@ -666,6 +696,7 @@
mTimingSessions.delete(userId);
mInQuotaAlarmListeners.delete(userId);
mExecutionStatsCache.delete(userId);
+ mUidToPackageCache.clear();
}
private boolean isUidInForeground(int uid) {
@@ -678,7 +709,7 @@
}
/** @return true if the job was started while the app was in the TOP state. */
- private boolean isTopStartedJob(@NonNull final JobStatus jobStatus) {
+ private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
return mTopStartedJobs.contains(jobStatus);
}
@@ -695,14 +726,14 @@
return jobStatus.getStandbyBucket();
}
- private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
+ @VisibleForTesting
+ boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
final int standbyBucket = getEffectiveStandbyBucket(jobStatus);
- Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
// A job is within quota if one of the following is true:
// 1. it was started while the app was in the TOP state
// 2. the app is currently in the foreground
// 3. the app overall is within its quota
- return isTopStartedJob(jobStatus)
+ return isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
@@ -1081,7 +1112,9 @@
if (earliestEndElapsed == Long.MAX_VALUE) {
// Couldn't find a good time to clean up. Maybe this was called after we deleted all
// timing sessions.
- if (DEBUG) Slog.d(TAG, "Didn't find a time to schedule cleanup");
+ if (DEBUG) {
+ Slog.d(TAG, "Didn't find a time to schedule cleanup");
+ }
return;
}
// Need to keep sessions for all apps up to the max period, regardless of their current
@@ -1095,15 +1128,19 @@
mNextCleanupTimeElapsed = nextCleanupElapsed;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP,
mSessionCleanupAlarmListener, mHandler);
- if (DEBUG) Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed);
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed);
+ }
}
private void handleNewChargingStateLocked() {
final long nowElapsed = sElapsedRealtimeClock.millis();
final boolean isCharging = mChargeTracker.isCharging();
- if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
+ if (DEBUG) {
+ Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
+ }
// Deal with Timers first.
- mPkgTimers.forEach((t) -> t.onStateChanged(nowElapsed, isCharging));
+ mPkgTimers.forEach((t) -> t.onStateChangedLocked(nowElapsed, isCharging));
// Now update jobs.
maybeUpdateAllConstraintsLocked();
}
@@ -1140,7 +1177,7 @@
boolean changed = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i);
- if (isTopStartedJob(js)) {
+ if (isTopStartedJobLocked(js)) {
// Job was started while the app was in the TOP state so we should allow it to
// finish.
changed |= js.setQuotaConstraintSatisfied(true);
@@ -1282,7 +1319,9 @@
if (!alarmListener.isWaiting()
|| inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS
|| alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed) {
- if (DEBUG) Slog.d(TAG, "Scheduling start alarm for " + pkgString);
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling start alarm for " + pkgString);
+ }
// If the next time this app will have quota is at least 3 minutes before the
// alarm is supposed to go off, reschedule the alarm.
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed,
@@ -1430,8 +1469,8 @@
mUid = uid;
}
- void startTrackingJob(@NonNull JobStatus jobStatus) {
- if (isTopStartedJob(jobStatus)) {
+ void startTrackingJobLocked(@NonNull JobStatus jobStatus) {
+ if (isTopStartedJobLocked(jobStatus)) {
// We intentionally don't pay attention to fg state changes after a TOP job has
// started.
if (DEBUG) {
@@ -1440,27 +1479,28 @@
}
return;
}
- if (DEBUG) Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
- synchronized (mLock) {
- // Always track jobs, even when charging.
- mRunningBgJobs.add(jobStatus);
- if (shouldTrackLocked()) {
- mBgJobCount++;
- incrementJobCount(mPkg.userId, mPkg.packageName, 1);
- if (mRunningBgJobs.size() == 1) {
- // Started tracking the first job.
- mStartTimeElapsed = sElapsedRealtimeClock.millis();
- // Starting the timer means that all cached execution stats are now
- // incorrect.
- invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
- scheduleCutoff();
- }
+ if (DEBUG) {
+ Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
+ }
+ // Always track jobs, even when charging.
+ mRunningBgJobs.add(jobStatus);
+ if (shouldTrackLocked()) {
+ mBgJobCount++;
+ incrementJobCount(mPkg.userId, mPkg.packageName, 1);
+ if (mRunningBgJobs.size() == 1) {
+ // Started tracking the first job.
+ mStartTimeElapsed = sElapsedRealtimeClock.millis();
+ // Starting the timer means that all cached execution stats are now incorrect.
+ invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
+ scheduleCutoff();
}
}
}
void stopTrackingJob(@NonNull JobStatus jobStatus) {
- if (DEBUG) Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString());
+ if (DEBUG) {
+ Slog.v(TAG, "Stopping tracking of " + jobStatus.toShortString());
+ }
synchronized (mLock) {
if (mRunningBgJobs.size() == 0) {
// maybeStopTrackingJobLocked can be called when an app cancels a job, so a
@@ -1482,7 +1522,7 @@
* Stops tracking all jobs and cancels any pending alarms. This should only be called if
* the Timer is not going to be used anymore.
*/
- void dropEverything() {
+ void dropEverythingLocked() {
mRunningBgJobs.clear();
cancelCutoff();
}
@@ -1531,25 +1571,23 @@
return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid);
}
- void onStateChanged(long nowElapsed, boolean isQuotaFree) {
- synchronized (mLock) {
- if (isQuotaFree) {
- emitSessionLocked(nowElapsed);
- } else if (shouldTrackLocked()) {
- // Start timing from unplug.
- if (mRunningBgJobs.size() > 0) {
- mStartTimeElapsed = nowElapsed;
- // NOTE: this does have the unfortunate consequence that if the device is
- // repeatedly plugged in and unplugged, or an app changes foreground state
- // very frequently, the job count for a package may be artificially high.
- mBgJobCount = mRunningBgJobs.size();
- incrementJobCount(mPkg.userId, mPkg.packageName, mBgJobCount);
- // Starting the timer means that all cached execution stats are now
- // incorrect.
- invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
- // Schedule cutoff since we're now actively tracking for quotas again.
- scheduleCutoff();
- }
+ void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) {
+ if (isQuotaFree) {
+ emitSessionLocked(nowElapsed);
+ } else if (!isActive() && shouldTrackLocked()) {
+ // Start timing from unplug.
+ if (mRunningBgJobs.size() > 0) {
+ mStartTimeElapsed = nowElapsed;
+ // NOTE: this does have the unfortunate consequence that if the device is
+ // repeatedly plugged in and unplugged, or an app changes foreground state
+ // very frequently, the job count for a package may be artificially high.
+ mBgJobCount = mRunningBgJobs.size();
+ incrementJobCount(mPkg.userId, mPkg.packageName, mBgJobCount);
+ // Starting the timer means that all cached execution stats are now
+ // incorrect.
+ invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
+ // Schedule cutoff since we're now actively tracking for quotas again.
+ scheduleCutoff();
}
}
}
@@ -1604,7 +1642,6 @@
pw.println(js.toShortString());
}
}
-
pw.decreaseIndent();
}
@@ -1667,7 +1704,9 @@
@Override
public void onParoleStateChanged(final boolean isParoleOn) {
mInParole = isParoleOn;
- if (DEBUG) Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
+ if (DEBUG) {
+ Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
+ }
// Update job bookkeeping out of band.
BackgroundThread.getHandler().post(() -> {
synchronized (mLock) {
@@ -1712,7 +1751,9 @@
switch (msg.what) {
case MSG_REACHED_QUOTA: {
Package pkg = (Package) msg.obj;
- if (DEBUG) Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
+ if (DEBUG) {
+ Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
+ }
long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
pkg.packageName);
@@ -1737,7 +1778,9 @@
break;
}
case MSG_CLEAN_UP_SESSIONS:
- if (DEBUG) Slog.d(TAG, "Cleaning up timing sessions.");
+ if (DEBUG) {
+ Slog.d(TAG, "Cleaning up timing sessions.");
+ }
deleteObsoleteSessionsLocked();
maybeScheduleCleanupAlarmLocked();
@@ -1745,7 +1788,9 @@
case MSG_CHECK_PACKAGE: {
String packageName = (String) msg.obj;
int userId = msg.arg1;
- if (DEBUG) Slog.d(TAG, "Checking pkg " + string(userId, packageName));
+ if (DEBUG) {
+ Slog.d(TAG, "Checking pkg " + string(userId, packageName));
+ }
if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
mStateChangedListener.onControllerStateChanged();
}
@@ -1767,13 +1812,28 @@
isQuotaFree = false;
}
// Update Timers first.
- final int userIndex = mPkgTimers.indexOfKey(userId);
- if (userIndex != -1) {
- final int numPkgs = mPkgTimers.numPackagesForUser(userId);
- for (int p = 0; p < numPkgs; ++p) {
- Timer t = mPkgTimers.valueAt(userIndex, p);
- if (t != null) {
- t.onStateChanged(nowElapsed, isQuotaFree);
+ if (mPkgTimers.indexOfKey(userId) >= 0) {
+ ArraySet<String> packages = mUidToPackageCache.get(uid);
+ if (packages == null) {
+ try {
+ String[] pkgs = AppGlobals.getPackageManager()
+ .getPackagesForUid(uid);
+ if (pkgs != null) {
+ for (String pkg : pkgs) {
+ mUidToPackageCache.add(uid, pkg);
+ }
+ packages = mUidToPackageCache.get(uid);
+ }
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to get package list", e);
+ }
+ }
+ if (packages != null) {
+ for (int i = packages.size() - 1; i >= 0; --i) {
+ Timer t = mPkgTimers.get(userId, packages.valueAt(i));
+ if (t != null) {
+ t.onStateChangedLocked(nowElapsed, isQuotaFree);
+ }
}
}
}
@@ -1883,6 +1943,17 @@
pw.println(mForegroundUids.toString());
pw.println();
+ pw.println("Cached UID->package map:");
+ pw.increaseIndent();
+ for (int i = 0; i < mUidToPackageCache.size(); ++i) {
+ final int uid = mUidToPackageCache.keyAt(i);
+ pw.print(uid);
+ pw.print(": ");
+ pw.println(mUidToPackageCache.get(uid));
+ }
+ pw.decreaseIndent();
+ pw.println();
+
mTrackedJobs.forEach((jobs) -> {
for (int j = 0; j < jobs.size(); j++) {
final JobStatus js = jobs.valueAt(j);
@@ -1936,6 +2007,29 @@
}
}
}
+
+ pw.println("Cached execution stats:");
+ pw.increaseIndent();
+ for (int u = 0; u < mExecutionStatsCache.numUsers(); ++u) {
+ final int userId = mExecutionStatsCache.keyAt(u);
+ for (int p = 0; p < mExecutionStatsCache.numPackagesForUser(userId); ++p) {
+ final String pkgName = mExecutionStatsCache.keyAt(u, p);
+ ExecutionStats[] stats = mExecutionStatsCache.valueAt(u, p);
+
+ pw.println(string(userId, pkgName));
+ pw.increaseIndent();
+ for (int i = 0; i < stats.length; ++i) {
+ ExecutionStats executionStats = stats[i];
+ if (executionStats != null) {
+ pw.print(JobStatus.bucketName(i));
+ pw.print(": ");
+ pw.println(executionStats);
+ }
+ }
+ pw.decreaseIndent();
+ }
+ }
+ pw.decreaseIndent();
}
@Override
@@ -1995,6 +2089,49 @@
}
}
+ ExecutionStats[] stats = mExecutionStatsCache.get(userId, pkgName);
+ if (stats != null) {
+ for (int bucketIndex = 0; bucketIndex < stats.length; ++bucketIndex) {
+ ExecutionStats es = stats[bucketIndex];
+ if (es == null) {
+ continue;
+ }
+ final long esToken = proto.start(
+ StateControllerProto.QuotaController.PackageStats.EXECUTION_STATS);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.STANDBY_BUCKET,
+ bucketIndex);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.EXPIRATION_TIME_ELAPSED,
+ es.expirationTimeElapsed);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.WINDOW_SIZE_MS,
+ es.windowSizeMs);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.EXECUTION_TIME_IN_WINDOW_MS,
+ es.executionTimeInWindowMs);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_WINDOW,
+ es.bgJobCountInWindow);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.EXECUTION_TIME_IN_MAX_PERIOD_MS,
+ es.executionTimeInMaxPeriodMs);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_MAX_PERIOD,
+ es.bgJobCountInMaxPeriod);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED,
+ es.quotaCutoffTimeElapsed);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_EXPIRATION_TIME_ELAPSED,
+ es.jobCountExpirationTimeElapsed);
+ proto.write(
+ StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME,
+ es.jobCountInAllowedTime);
+ proto.end(esToken);
+ }
+ }
+
proto.end(psToken);
}
}
diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
index b4b6160..88ff6e7 100644
--- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
+++ b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
@@ -28,33 +28,16 @@
private static final String TAG = "GnssCapabilitiesProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- // Bit masks for capabilities in {@link android.location.GnssCapabilities}.
- private static final long GNSS_CAPABILITY_LOW_POWER_MODE =
- 1L << GnssCapabilities.LOW_POWER_MODE;
- private static final long GNSS_CAPABILITY_SATELLITE_BLACKLIST =
- 1L << GnssCapabilities.SATELLITE_BLACKLIST;
- private static final long GNSS_CAPABILITY_GEOFENCING = 1L << GnssCapabilities.GEOFENCING;
- private static final long GNSS_CAPABILITY_MEASUREMENTS = 1L << GnssCapabilities.MEASUREMENTS;
- private static final long GNSS_CAPABILITY_NAV_MESSAGES = 1L << GnssCapabilities.NAV_MESSAGES;
- private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS =
- 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS;
- private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_LOS_SATS =
- 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS;
- private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH =
- 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH;
- private static final long GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_REFLECTING_PLANE =
- 1L << GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
-
private static final long GNSS_CAPABILITIES_TOP_HAL =
- GNSS_CAPABILITY_LOW_POWER_MODE | GNSS_CAPABILITY_SATELLITE_BLACKLIST
- | GNSS_CAPABILITY_GEOFENCING | GNSS_CAPABILITY_MEASUREMENTS
- | GNSS_CAPABILITY_NAV_MESSAGES;
+ GnssCapabilities.LOW_POWER_MODE | GnssCapabilities.SATELLITE_BLACKLIST
+ | GnssCapabilities.GEOFENCING | GnssCapabilities.MEASUREMENTS
+ | GnssCapabilities.NAV_MESSAGES;
private static final long GNSS_CAPABILITIES_SUB_HAL_MEASUREMENT_CORRECTIONS =
- GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS
- | GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_LOS_SATS
- | GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH
- | GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
+ GnssCapabilities.MEASUREMENT_CORRECTIONS
+ | GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS
+ | GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH
+ | GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
// Capabilities in {@link android.location.GnssCapabilities} supported by GNSS chipset.
@GuardedBy("this")
@@ -79,20 +62,20 @@
long gnssCapabilities = 0;
if (hasCapability(topHalCapabilities,
GnssLocationProvider.GPS_CAPABILITY_LOW_POWER_MODE)) {
- gnssCapabilities |= GNSS_CAPABILITY_LOW_POWER_MODE;
+ gnssCapabilities |= GnssCapabilities.LOW_POWER_MODE;
}
if (hasCapability(topHalCapabilities,
GnssLocationProvider.GPS_CAPABILITY_SATELLITE_BLACKLIST)) {
- gnssCapabilities |= GNSS_CAPABILITY_SATELLITE_BLACKLIST;
+ gnssCapabilities |= GnssCapabilities.SATELLITE_BLACKLIST;
}
if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_GEOFENCING)) {
- gnssCapabilities |= GNSS_CAPABILITY_GEOFENCING;
+ gnssCapabilities |= GnssCapabilities.GEOFENCING;
}
if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS)) {
- gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENTS;
+ gnssCapabilities |= GnssCapabilities.MEASUREMENTS;
}
if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) {
- gnssCapabilities |= GNSS_CAPABILITY_NAV_MESSAGES;
+ gnssCapabilities |= GnssCapabilities.NAV_MESSAGES;
}
synchronized (this) {
@@ -110,18 +93,18 @@
* {@link android.location.GnssCapabilities}.
*/
void setSubHalMeasurementCorrectionsCapabilities(int measurementCorrectionsCapabilities) {
- long gnssCapabilities = GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS;
+ long gnssCapabilities = GnssCapabilities.MEASUREMENT_CORRECTIONS;
if (hasCapability(measurementCorrectionsCapabilities,
GnssMeasurementCorrectionsProvider.CAPABILITY_LOS_SATS)) {
- gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_LOS_SATS;
+ gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS;
}
if (hasCapability(measurementCorrectionsCapabilities,
GnssMeasurementCorrectionsProvider.CAPABILITY_EXCESS_PATH_LENGTH)) {
- gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH;
+ gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH;
}
if (hasCapability(measurementCorrectionsCapabilities,
GnssMeasurementCorrectionsProvider.CAPABILITY_REFLECTING_PLANE)) {
- gnssCapabilities |= GNSS_CAPABILITY_MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
+ gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
}
synchronized (this) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 96fc6ec..d93dddfc 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -66,6 +66,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.StatsLog;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
@@ -332,9 +333,16 @@
// true if low power mode for the GNSS chipset is part of the latest request.
private boolean mLowPowerMode = false;
- // true if we started navigation
+ // true if we started navigation in the HAL, only change value of this in setStarted
private boolean mStarted;
+ // for logging of latest change, and warning of ongoing location after a stop
+ private long mStartedChangedElapsedRealtime;
+
+ // threshold for delay in GNSS engine turning off before warning & error
+ private static final long LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS = 2 * 1000;
+ private static final long LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS = 15 * 1000;
+
// capabilities reported through the top level IGnssCallback.hal
private volatile int mTopHalCapabilities;
@@ -927,6 +935,9 @@
mGnssMeasurementsProvider.onGpsEnabledChanged();
mGnssNavigationMessageProvider.onGpsEnabledChanged();
mGnssBatchingProvider.enable();
+ if (mGnssVisibilityControl != null) {
+ mGnssVisibilityControl.onGpsEnabledChanged(mEnabled);
+ }
} else {
mEnabled = false;
Log.w(TAG, "Failed to enable location provider");
@@ -943,6 +954,9 @@
mAlarmManager.cancel(mWakeupIntent);
mAlarmManager.cancel(mTimeoutIntent);
+ if (mGnssVisibilityControl != null) {
+ mGnssVisibilityControl.onGpsEnabledChanged(mEnabled);
+ }
mGnssBatchingProvider.disable();
// do this before releasing wakelock
native_cleanup();
@@ -1192,7 +1206,7 @@
if (DEBUG) Log.d(TAG, "startNavigating");
mTimeToFirstFix = 0;
mLastFixTime = 0;
- mStarted = true;
+ setStarted(true);
mPositionMode = GPS_POSITION_MODE_STANDALONE;
// Notify about suppressed output, if speed limit was previously exceeded.
// Elsewhere, we check again with every speed output reported.
@@ -1230,12 +1244,12 @@
mLowPowerMode = mProviderRequest.lowPowerMode;
if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
interval, 0, 0, mLowPowerMode)) {
- mStarted = false;
+ setStarted(false);
Log.e(TAG, "set_position_mode failed in startNavigating()");
return;
}
if (!native_start()) {
- mStarted = false;
+ setStarted(false);
Log.e(TAG, "native_start failed in startNavigating()");
return;
}
@@ -1258,7 +1272,7 @@
private void stopNavigating() {
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
- mStarted = false;
+ setStarted(false);
native_stop();
mLastFixTime = 0;
// native_stop() may reset the position mode in hardware.
@@ -1270,6 +1284,13 @@
}
}
+ private void setStarted(boolean started) {
+ if (mStarted != started) {
+ mStarted = started;
+ mStartedChangedElapsedRealtime = SystemClock.elapsedRealtime();
+ }
+ }
+
private void hibernate() {
// stop GPS until our next fix interval arrives
stopNavigating();
@@ -1319,6 +1340,21 @@
mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
}
}
+ } else {
+ // Warn or error about long delayed GNSS engine shutdown as this generally wastes
+ // power and sends location when not expected.
+ long locationAfterStartedFalseMillis =
+ SystemClock.elapsedRealtime() - mStartedChangedElapsedRealtime;
+ if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS) {
+ String logMessage = "Unexpected GNSS Location report "
+ + TimeUtils.formatDuration(locationAfterStartedFalseMillis)
+ + " after location turned off";
+ if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS) {
+ Log.e(TAG, logMessage);
+ } else {
+ Log.w(TAG, logMessage);
+ }
+ }
}
mLastFixTime = SystemClock.elapsedRealtime();
@@ -1538,7 +1574,7 @@
private void restartLocationRequest() {
if (DEBUG) Log.d(TAG, "restartLocationRequest");
- mStarted = false;
+ setStarted(false);
updateRequirements();
}
@@ -2152,7 +2188,10 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
- s.append(" mStarted=").append(mStarted).append('\n');
+ s.append(" mStarted=").append(mStarted).append(" (changed ");
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+ - mStartedChangedElapsedRealtime, s);
+ s.append(" ago)").append('\n');
s.append(" mFixInterval=").append(mFixInterval).append('\n');
s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n');
s.append(" mGnssMeasurementsProvider.isRegistered()=")
@@ -2176,7 +2215,7 @@
s.append("MEASUREMENT_CORRECTIONS ");
}
s.append(")\n");
- if (mGnssMeasurementCorrectionsProvider.isAvailableInPlatform()) {
+ if (hasCapability(GPS_CAPABILITY_MEASUREMENT_CORRECTIONS)) {
s.append(" SubHal=MEASUREMENT_CORRECTIONS[");
s.append(mGnssMeasurementCorrectionsProvider.toStringCapabilities());
s.append("]\n");
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index 24ee389..8391f9d 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -23,13 +23,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.location.LocationManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -49,18 +46,14 @@
private static final String TAG = "GnssVisibilityControl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- // Constants related to non-framework (NFW) location access permission proxy apps.
- private static final String NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX =
- ".NonFrameworkLocationAccessActivity";
- private static final String NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX =
- ".intent.action.NON_FRAMEWORK_LOCATION_ACCESS";
- private static final String NFW_INTENT_TYPE = "text/plain";
-
private static final String LOCATION_PERMISSION_NAME =
"android.permission.ACCESS_FINE_LOCATION";
private static final String[] NO_LOCATION_ENABLED_PROXY_APPS = new String[0];
+ // Max wait time for synchronous method onGpsEnabledChanged() to run.
+ private static final long ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS = 3 * 1000;
+
// Wakelocks
private static final String WAKELOCK_KEY = TAG;
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
@@ -72,7 +65,7 @@
private final Handler mHandler;
private final Context mContext;
- private boolean mIsDeviceLocationSettingsEnabled;
+ private boolean mIsGpsEnabled;
// Number of non-framework location access proxy apps is expected to be small (< 5).
private static final int ARRAY_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -95,6 +88,30 @@
runOnHandler(this::handleInitialize);
}
+ void onGpsEnabledChanged(boolean isEnabled) {
+ // The GnssLocationProvider's methods: handleEnable() calls this method after native_init()
+ // and handleDisable() calls this method before native_cleanup(). This method must be
+ // executed synchronously so that the NFW location access permissions are disabled in
+ // the HAL before native_cleanup() method is called.
+ //
+ // NOTE: Since improper use of runWithScissors() method can result in deadlocks, the method
+ // doc recommends limiting its use to cases where some initialization steps need to be
+ // executed in sequence before continuing which fits this scenario.
+ if (mHandler.runWithScissors(() -> handleGpsEnabledChanged(isEnabled),
+ ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS)) {
+ return;
+ }
+
+ // After timeout, the method remains posted in the queue and hence future enable/disable
+ // calls to this method will all get executed in the correct sequence. But this timeout
+ // situation should not even arise because runWithScissors() will run in the caller's
+ // thread without blocking as it is the same thread as mHandler's thread.
+ if (!isEnabled) {
+ Log.w(TAG, "Native call to disable non-framework location access in GNSS HAL may"
+ + " get executed after native_cleanup().");
+ }
+ }
+
void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
}
@@ -110,12 +127,6 @@
private void handleInitialize() {
disableNfwLocationAccess(); // Disable until config properties are loaded.
listenForProxyAppsPackageUpdates();
- listenForDeviceLocationSettingsUpdate();
- mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings();
- }
-
- private boolean getDeviceLocationSettings() {
- return mContext.getSystemService(LocationManager.class).isLocationEnabled();
}
private void listenForProxyAppsPackageUpdates() {
@@ -145,18 +156,6 @@
}, UserHandle.ALL, intentFilter, null, mHandler);
}
- private void listenForDeviceLocationSettingsUpdate() {
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
- true,
- new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- handleDeviceLocationSettingsUpdated();
- }
- }, UserHandle.USER_ALL);
- }
-
private void handleProxyAppPackageUpdate(String pkgName, String action) {
final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
if (locationPermission == null) {
@@ -213,22 +212,21 @@
return false;
}
- private void handleDeviceLocationSettingsUpdated() {
- final boolean enabled = getDeviceLocationSettings();
- Log.i(TAG, "Device location settings enabled: " + enabled);
+ private void handleGpsEnabledChanged(boolean isEnabled) {
+ if (DEBUG) Log.d(TAG, "handleGpsEnabledChanged, isEnabled: " + isEnabled);
- if (mIsDeviceLocationSettingsEnabled == enabled) {
+ if (mIsGpsEnabled == isEnabled) {
return;
}
- mIsDeviceLocationSettingsEnabled = enabled;
- if (!mIsDeviceLocationSettingsEnabled) {
+ mIsGpsEnabled = isEnabled;
+ if (!mIsGpsEnabled) {
disableNfwLocationAccess();
return;
}
- // When device location settings was disabled, we already set the proxy app list
- // to empty in GNSS HAL. Update only if the proxy app list is not empty.
+ // When GNSS was disabled, we already set the proxy app list to empty in GNSS HAL.
+ // Update only if the proxy app list is not empty.
String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps();
if (locationPermissionEnabledProxyApps.length != 0) {
setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps);
@@ -290,6 +288,18 @@
return "<Unknown>";
}
}
+
+ private boolean isRequestAccepted() {
+ return mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED;
+ }
+
+ private boolean isRequestAttributedToProxyApp() {
+ return !TextUtils.isEmpty(mProxyAppPackageName);
+ }
+
+ private boolean isEmergencyRequestNotification() {
+ return mInEmergencyMode && !isRequestAttributedToProxyApp();
+ }
}
private void handlePermissionsChanged(int uid) {
@@ -335,7 +345,7 @@
}
private void updateNfwLocationAccessProxyAppsInGnssHal() {
- if (!mIsDeviceLocationSettingsEnabled) {
+ if (!mIsGpsEnabled) {
return; // Keep non-framework location access disabled.
}
setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps());
@@ -378,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;
@@ -425,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 af299cf..69efd02 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -24,10 +24,14 @@
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import android.annotation.Nullable;
+import android.net.INetd;
import android.net.NetworkStats;
+import android.net.util.NetdService;
+import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemClock;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
@@ -64,7 +68,10 @@
private boolean mUseBpfStats;
+ private INetd mNetdService;
+
// A persistent Snapshot since device start for eBPF stats
+ @GuardedBy("mPersistSnapshot")
private final NetworkStats mPersistSnapshot;
// TODO: only do adjustments in NetworkStatsService and remove this.
@@ -272,6 +279,19 @@
return stats;
}
+ @GuardedBy("mPersistSnapshot")
+ private void requestSwapActiveStatsMapLocked() throws RemoteException {
+ // Ask netd to do a active map stats swap. When the binder call successfully returns,
+ // the system server should be able to safely read and clean the inactive map
+ // without race problem.
+ if (mUseBpfStats) {
+ if (mNetdService == null) {
+ mNetdService = NetdService.getInstance();
+ }
+ mNetdService.trafficSwapActiveStatsMap();
+ }
+ }
+
// TODO: delete the lastStats parameter
private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces,
int limitTag, NetworkStats lastStats) throws IOException {
@@ -284,15 +304,24 @@
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
if (mUseBpfStats) {
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
- null, TAG_ALL, mUseBpfStats) != 0) {
- throw new IOException("Failed to parse network stats");
+ synchronized (mPersistSnapshot) {
+ try {
+ requestSwapActiveStatsMapLocked();
+ } catch (RemoteException e) {
+ throw new IOException(e);
+ }
+ // Stats are always read from the inactive map, so they must be read after the
+ // swap
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
+ null, TAG_ALL, mUseBpfStats) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
+ mPersistSnapshot.combineAllValues(stats);
+ NetworkStats result = mPersistSnapshot.clone();
+ result.filter(limitUid, limitIfaces, limitTag);
+ return result;
}
- mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
- mPersistSnapshot.combineAllValues(stats);
- NetworkStats result = mPersistSnapshot.clone();
- result.filter(limitUid, limitIfaces, limitTag);
- return result;
} else {
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
limitIfaces, limitTag, mUseBpfStats) != 0) {
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 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 2e8e65b..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
@@ -2863,8 +2881,7 @@
// Reset notification preferences
if (!fromApp) {
- mPreferencesHelper.onPackagesChanged(
- true, UserHandle.getCallingUserId(), packages, uids);
+ mPreferencesHelper.clearData(packageName, uid);
}
handleSavePolicyFile();
@@ -4783,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
@@ -4808,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/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 642fa7f..9e16632 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1717,6 +1717,23 @@
}
}
+ public void clearData(String pkg, int uid) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
+ if (p != null) {
+ p.channels = new ArrayMap<>();
+ p.groups = new ArrayMap<>();
+ p.delegate = null;
+ p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
+ p.allowBubble = DEFAULT_ALLOW_BUBBLE;
+ p.importance = DEFAULT_IMPORTANCE;
+ p.priority = DEFAULT_PRIORITY;
+ p.visibility = DEFAULT_VISIBILITY;
+ p.showBadge = DEFAULT_SHOW_BADGE;
+ }
+ }
+ }
+
private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
return new LogMaker(
com.android.internal.logging.nano.MetricsProto.MetricsEvent
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c4d4106..3c336ea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -952,6 +952,9 @@
ActivityInfo mInstantAppInstallerActivity;
final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
+ private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
+ mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
+
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<>();
@@ -1319,6 +1322,11 @@
static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
static final int ENABLE_ROLLBACK_STATUS = 21;
static final int ENABLE_ROLLBACK_TIMEOUT = 22;
+ static final int DEFERRED_NO_KILL_POST_DELETE = 23;
+ static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
+
+ static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
+ static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -1525,6 +1533,20 @@
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
} break;
+ case DEFERRED_NO_KILL_POST_DELETE: {
+ synchronized (mInstallLock) {
+ InstallArgs args = (InstallArgs) msg.obj;
+ if (args != null) {
+ args.doPostDeleteLI(true);
+ }
+ }
+ } break;
+ case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
+ String packageName = (String) msg.obj;
+ if (packageName != null) {
+ notifyInstallObserver(packageName);
+ }
+ } break;
case WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
@@ -1791,7 +1813,10 @@
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver) {
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
+ final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
+
+ if (succeeded) {
// Send the removed broadcasts
if (res.removedInfo != null) {
res.removedInfo.sendPackageRemovedBroadcasts(killApp);
@@ -1819,8 +1844,6 @@
mPermissionCallback);
}
- final boolean update = res.removedInfo != null
- && res.removedInfo.removedPackage != null;
final String installerPackageName =
res.installerPackageName != null
? res.installerPackageName
@@ -2029,11 +2052,18 @@
getUnknownSourcesSettings());
// Remove the replaced package's older resources safely now
- // We delete after a gc for applications on sdcard.
- if (res.removedInfo != null && res.removedInfo.args != null) {
- Runtime.getRuntime().gc();
- synchronized (mInstallLock) {
- res.removedInfo.args.doPostDeleteLI(true);
+ InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
+ if (args != null) {
+ if (!killApp) {
+ // If we didn't kill the app, defer the deletion of code/resource files, since
+ // they may still be in use by the running application. This mitigates problems
+ // in cases where resources or code is loaded by a new Activity before
+ // ApplicationInfo changes have propagated to all application threads.
+ scheduleDeferredNoKillPostDelete(args);
+ } else {
+ synchronized (mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
}
} else {
// Force a gc to clear up things. Ask for a background one, it's fine to go on
@@ -2056,18 +2086,62 @@
}
}
- // If someone is watching installs - notify them
+ final boolean deferInstallObserver = succeeded && update && !killApp;
+ if (deferInstallObserver) {
+ scheduleDeferredNoKillInstallObserver(res, installObserver);
+ } else {
+ notifyInstallObserver(res, installObserver);
+ }
+ }
+
+ @Override
+ public void notifyPackagesReplacedReceived(String[] packages) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ for (String packageName : packages) {
+ PackageSetting setting = mSettings.mPackages.get(packageName);
+ if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) {
+ notifyInstallObserver(packageName);
+ }
+ }
+ }
+
+ private void notifyInstallObserver(String packageName) {
+ Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
+ mNoKillInstallObservers.remove(packageName);
+
+ if (pair != null) {
+ notifyInstallObserver(pair.first, pair.second);
+ }
+ }
+
+ private void notifyInstallObserver(PackageInstalledInfo info,
+ IPackageInstallObserver2 installObserver) {
if (installObserver != null) {
try {
- Bundle extras = extrasForInstallResult(res);
- installObserver.onPackageInstalled(res.name, res.returnCode,
- res.returnMsg, extras);
+ Bundle extras = extrasForInstallResult(info);
+ installObserver.onPackageInstalled(info.name, info.returnCode,
+ info.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
+ private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
+ Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
+ mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
+ }
+
+ private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
+ IPackageInstallObserver2 observer) {
+ String packageName = info.pkg.packageName;
+ mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
+ Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
+ mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
+ }
+
/**
* Gets the type of the external storage a package is installed on.
* @param packageVolume The storage volume of the package.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6b804df..33dd48a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -25,6 +25,8 @@
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.role.IRoleManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -75,6 +77,7 @@
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ShellCommand;
@@ -115,6 +118,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -2460,19 +2464,37 @@
}
}
+ String pkgName;
String component = getNextArg();
- ComponentName componentName =
- component != null ? ComponentName.unflattenFromString(component) : null;
-
- if (componentName == null) {
- pw.println("Error: component name not specified or invalid");
- return 1;
+ if (component.indexOf('/') < 0) {
+ // No component specified, so assume it's just a package name.
+ pkgName = component;
+ } else {
+ ComponentName componentName =
+ component != null ? ComponentName.unflattenFromString(component) : null;
+ if (componentName == null) {
+ pw.println("Error: invalid component name");
+ return 1;
+ }
+ pkgName = componentName.getPackageName();
}
+
+ final CompletableFuture<Boolean> future = new CompletableFuture<>();
+ final RemoteCallback callback = new RemoteCallback(res -> future.complete(res != null));
try {
- mInterface.setHomeActivity(componentName, userId);
- pw.println("Success");
- return 0;
+ IRoleManager roleManager = android.app.role.IRoleManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.ROLE_SERVICE));
+ roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, pkgName,
+ 0, userId, callback);
+ boolean success = future.get();
+ if (success) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println("Error: Failed to set default home.");
+ return 1;
+ }
} catch (Exception e) {
pw.println(e.toString());
return 1;
@@ -3161,6 +3183,10 @@
pw.println("");
pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT");
pw.println(" Set the default home activity (aka launcher).");
+ pw.println(" TARGET-COMPONENT can be a package name (com.package.my) or a full");
+ pw.println(" component (com.package.my/component.name). However, only the package name");
+ pw.println(" matters: the actual component used will be determined automatically from");
+ pw.println(" the package.");
pw.println("");
pw.println(" set-installer PACKAGE INSTALLER");
pw.println(" Set installer package name");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index db2fba9..2a9cb89 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2687,7 +2687,7 @@
private void writePackageListLPrInternal(int creatingUserId) {
// Only derive GIDs for active users (not dying)
- final List<UserInfo> users = UserManagerService.getInstance().getUsers(true);
+ final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true);
int[] userIds = new int[users.size()];
for (int i = 0; i < userIds.length; i++) {
userIds[i] = users.get(i).id;
@@ -4357,10 +4357,26 @@
return pkgSetting.getHarmfulAppWarning(userId);
}
+ /**
+ * Return all users on the device, including partial or dying users.
+ * @param userManager UserManagerService instance
+ * @return the list of users
+ */
private static List<UserInfo> getAllUsers(UserManagerService userManager) {
+ return getUsers(userManager, false);
+ }
+
+ /**
+ * Return the list of users on the device. Clear the calling identity before calling into
+ * UserManagerService.
+ * @param userManager UserManagerService instance
+ * @param excludeDying Indicates whether to exclude any users marked for deletion.
+ * @return the list of users
+ */
+ private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) {
long id = Binder.clearCallingIdentity();
try {
- return userManager.getUsers(false);
+ return userManager.getUsers(excludeDying);
} catch (NullPointerException npe) {
// packagemanager not yet initialized
} finally {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9b4293d48..d624a85 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -310,6 +310,7 @@
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
+ private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -615,6 +616,8 @@
private boolean mPerDisplayFocusEnabled = false;
private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;
+ private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
+
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
@@ -782,6 +785,9 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this,
+ UserHandle.USER_ALL);
updateSettings();
}
@@ -1105,16 +1111,16 @@
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
- goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+ goToSleepFromPowerButton(eventTime, 0);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
- goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+ goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
break;
case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
- goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
- launchHomeFromHotKey(DEFAULT_DISPLAY);
+ if (goToSleepFromPowerButton(eventTime,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
+ launchHomeFromHotKey(DEFAULT_DISPLAY);
+ }
break;
case SHORT_PRESS_POWER_GO_HOME:
shortPressPowerGoHome();
@@ -1137,6 +1143,35 @@
}
}
+ /**
+ * Sends the device to sleep as a result of a power button press.
+ *
+ * @return True if the was device was sent to sleep, false if sleep was suppressed.
+ */
+ private boolean goToSleepFromPowerButton(long eventTime, int flags) {
+ // Before we actually go to sleep, we check the last wakeup reason.
+ // If the device very recently woke up from a gesture (like user lifting their device)
+ // then ignore the sleep instruction. This is because users have developed
+ // a tendency to hit the power button immediately when they pick up their device, and we
+ // don't want to put the device back to sleep in those cases.
+ final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();
+ if (lastWakeUp != null && lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE) {
+ final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
+ POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
+ final long now = SystemClock.uptimeMillis();
+ if (mPowerButtonSuppressionDelayMillis > 0
+ && (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {
+ Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "
+ + (now - lastWakeUp.wakeTime) + "ms");
+ return false;
+ }
+ }
+
+ goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
+ return true;
+ }
+
private void goToSleep(long eventTime, int reason, int flags) {
mRequestedOrGoingToSleep = true;
mPowerManager.goToSleep(eventTime, reason, flags);
@@ -1981,6 +2016,9 @@
mRingerToggleChord = Settings.Secure.getIntForUser(resolver,
Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
UserHandle.USER_CURRENT);
+ mPowerButtonSuppressionDelayMillis = Settings.Global.getInt(resolver,
+ Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
+ POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
if (!mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
@@ -3594,11 +3632,15 @@
}
}
}
- } else if (ExtconUEventObserver.extconExists()) {
+ } else if (ExtconUEventObserver.extconExists()
+ && ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) {
HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
plugged = observer.init();
mHDMIObserver = observer;
+ } else if (localLOGV) {
+ Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
}
+
// This dance forces the code in setHdmiPlugged to run.
// Always do this so the sticky intent is stuck (to false) if there is no hdmi.
mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
@@ -5658,7 +5700,8 @@
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
- private final ExtconInfo mHdmi = new ExtconInfo("hdmi");
+ private static final String NAME = "hdmi";
+ private final ExtconInfo mHdmi = new ExtconInfo(NAME);
private boolean init() {
boolean plugged = false;
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 5e829b2..a65a812 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -76,6 +76,12 @@
private final AtomicBoolean mRequested;
/**
+ * Monotonously increasing ID for the requests sent.
+ */
+ @VisibleForTesting
+ protected int mRequestId;
+
+ /**
* {@link android.service.attention.AttentionService} API timeout.
*/
private long mMaxAttentionApiTimeoutMillis;
@@ -105,36 +111,13 @@
private AtomicLong mConsecutiveTimeoutExtendedCount = new AtomicLong(0);
@VisibleForTesting
- final AttentionCallbackInternal mCallback = new AttentionCallbackInternal() {
- @Override
- public void onSuccess(int result, long timestamp) {
- Slog.v(TAG, "onSuccess: " + result);
- if (mRequested.getAndSet(false)) {
- synchronized (mLock) {
- if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
- if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
- return;
- }
- if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
- mOnUserAttention.run();
- } else {
- resetConsecutiveExtensionCount();
- }
- }
- }
- }
-
- @Override
- public void onFailure(int error) {
- Slog.i(TAG, "Failed to check attention: " + error);
- mRequested.set(false);
- }
- };
+ AttentionCallbackInternalImpl mCallback;
public AttentionDetector(Runnable onUserAttention, Object lock) {
mOnUserAttention = onUserAttention;
mLock = lock;
mRequested = new AtomicBoolean(false);
+ mRequestId = 0;
// Device starts with an awake state upon boot.
mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -196,9 +179,7 @@
return nextScreenDimming;
} else if (mRequested.get()) {
if (DEBUG) {
- // TODO(b/128134941): consider adding a member ID increasing counter in
- // AttentionCallbackInternal to track this better.
- Slog.d(TAG, "Pending attention callback, wait.");
+ Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait.");
}
return whenToCheck;
}
@@ -208,6 +189,8 @@
// This means that we must assume that the request was successful, and then cancel it
// afterwards if AttentionManager couldn't deliver it.
mRequested.set(true);
+ mRequestId++;
+ mCallback = new AttentionCallbackInternalImpl(mRequestId);
final boolean sent = mAttentionManager.checkAttention(getAttentionTimeout(), mCallback);
if (!sent) {
mRequested.set(false);
@@ -301,4 +284,40 @@
pw.print(" mAttentionServiceSupported=" + isAttentionServiceSupported());
pw.print(" mRequested=" + mRequested);
}
+
+ @VisibleForTesting
+ final class AttentionCallbackInternalImpl extends AttentionCallbackInternal {
+ private final int mId;
+
+ AttentionCallbackInternalImpl(int id) {
+ this.mId = id;
+ }
+
+ @Override
+ public void onSuccess(int result, long timestamp) {
+ Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId);
+ // If we don't check for request ID it's possible to get into a loop: success leads
+ // to the onUserAttention(), which in turn triggers updateUserActivity(), which will
+ // call back onSuccess() instantaneously if there is a cached value, and circle repeats.
+ if (mId == mRequestId && mRequested.getAndSet(false)) {
+ synchronized (mLock) {
+ if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+ if (DEBUG) Slog.d(TAG, "Device slept before receiving callback.");
+ return;
+ }
+ if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) {
+ mOnUserAttention.run();
+ } else {
+ resetConsecutiveExtensionCount();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(int error) {
+ Slog.i(TAG, "Failed to check attention: " + error + ", ID: " + mId);
+ mRequested.set(false);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cfe11bf..e2bbb2d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -52,6 +52,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
+import android.os.PowerManager.WakeData;
import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
@@ -4850,6 +4851,12 @@
}
}
+ private PowerManager.WakeData getLastWakeupInternal() {
+ synchronized (mLock) {
+ return new WakeData(mLastWakeTime, mLastWakeReason);
+ }
+ }
+
private final class LocalService extends PowerManagerInternal {
@Override
public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {
@@ -4971,5 +4978,10 @@
public boolean wasDeviceIdleFor(long ms) {
return wasDeviceIdleForInternal(ms);
}
+
+ @Override
+ public WakeData getLastWakeup() {
+ return getLastWakeupInternal();
+ }
}
}
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/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 10afbef..0891ba4 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -99,7 +99,7 @@
* data for Tron, logcat, event logs and {@link android.app.WaitResult}.
*
* Tests:
- * atest CtsActivityManagerDeviceTestCases:ActivityMetricsLoggerTests
+ * atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests
*/
class ActivityMetricsLogger {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ee6cf54..0820b0d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3372,6 +3372,11 @@
if (stack.inFreeformWindowingMode()) {
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ } else if (stack.getParent().inFreeformWindowingMode()) {
+ // If the window is on a freeform display, set it to undefined. It will be
+ // resolved to freeform and it can adjust windowing mode when the display mode
+ // changes in runtime.
+ stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
} else {
stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index a7a793f..767327a 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -102,7 +102,9 @@
}
void removeSurface() {
- getPendingTransaction().remove(mDimLayer);
+ if (mDimLayer != null && mDimLayer.isValid()) {
+ getPendingTransaction().remove(mDimLayer);
+ }
mDimLayer = null;
}
}
@@ -305,7 +307,9 @@
if (!mDimState.mDimming) {
if (!mDimState.mAnimateExit) {
- t.remove(mDimState.mDimLayer);
+ if (mDimState.mDimLayer.isValid()) {
+ t.remove(mDimState.mDimLayer);
+ }
} else {
startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index bd874ba..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;
@@ -822,6 +821,10 @@
(int) attrs.hideTimeoutMilliseconds,
AccessibilityManager.FLAG_CONTENT_TEXT);
attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
+ // Toast can show with below conditions when the screen is locked.
+ if (canToastShowWhenLocked(callingPid)) {
+ attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+ }
break;
}
@@ -832,6 +835,16 @@
}
/**
+ * @return {@code true} if the calling activity initiate toast and is visible with
+ * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag.
+ */
+ boolean canToastShowWhenLocked(int callingPid) {
+ return mDisplayContent.forAllWindows(w -> {
+ return callingPid == w.mSession.mPid && w.isVisible() && w.canShowWhenLocked();
+ }, true /* traverseTopToBottom */);
+ }
+
+ /**
* Preflight adding a window to the system.
*
* Currently enforces that three window types are singletons per display:
@@ -1607,11 +1620,9 @@
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
final int top = cutoutSafeUnrestricted.bottom
- getNavigationBarHeight(rotation, uiMode);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
final int topNavBar = cutoutSafeUnrestricted.bottom
- - mExperiments.getNavigationBarFrameHeight();
+ - getNavigationBarFrameHeight(rotation, uiMode);
navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom);
- // EXPERIMENT END
displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -1634,11 +1645,7 @@
// Landscape screen; nav bar goes to the right.
final int left = cutoutSafeUnrestricted.right
- getNavigationBarWidth(rotation, uiMode);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- final int leftNavBar = cutoutSafeUnrestricted.right
- - mExperiments.getNavigationBarFrameWidth();
- navigationFrame.set(leftNavBar, 0, displayFrames.mUnrestricted.right, displayHeight);
- // EXPERIMENT END
+ navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -1661,11 +1668,7 @@
// Seascape screen; nav bar goes to the left.
final int right = cutoutSafeUnrestricted.left
+ getNavigationBarWidth(rotation, uiMode);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- final int rightNavBar = cutoutSafeUnrestricted.left
- + mExperiments.getNavigationBarFrameWidth();
- navigationFrame.set(displayFrames.mUnrestricted.left, 0, rightNavBar, displayHeight);
- // EXPERIMENT END
+ navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -1873,10 +1876,21 @@
}
}
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- // Offset the ime to avoid overlapping with the nav bar
- mExperiments.offsetWindowFramesForNavBar(mNavigationBarPosition, win);
- // EXPERIMENT END
+ // In case the navigation bar is on the bottom, we use the frame height instead of the
+ // regular height for the insets we send to the IME as we need some space to show
+ // additional buttons in SystemUI when the IME is up.
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ final int rotation = displayFrames.mRotation;
+ final int uimode = mService.mPolicy.getUiMode();
+ final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode)
+ - getNavigationBarHeight(rotation, uimode);
+ if (navHeightOffset > 0) {
+ cf.bottom -= navHeightOffset;
+ sf.bottom -= navHeightOffset;
+ vf.bottom -= navHeightOffset;
+ dcf.bottom -= navHeightOffset;
+ }
+ }
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
@@ -2609,6 +2623,7 @@
final int upsideDownRotation = displayRotation.getUpsideDownRotation();
final int landscapeRotation = displayRotation.getLandscapeRotation();
final int seascapeRotation = displayRotation.getSeascapeRotation();
+ final int uiMode = mService.mPolicy.getUiMode();
if (hasStatusBar()) {
mStatusBarHeightForRotation[portraitRotation] =
@@ -2632,6 +2647,14 @@
mNavigationBarHeightForRotationDefault[seascapeRotation] =
res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape);
+ // Height of the navigation bar frame when presented horizontally at bottom
+ mNavigationBarFrameHeightForRotationDefault[portraitRotation] =
+ mNavigationBarFrameHeightForRotationDefault[upsideDownRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height);
+ mNavigationBarFrameHeightForRotationDefault[landscapeRotation] =
+ mNavigationBarFrameHeightForRotationDefault[seascapeRotation] =
+ res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height_landscape);
+
// Width of the navigation bar when presented vertically along one side
mNavigationBarWidthForRotationDefault[portraitRotation] =
mNavigationBarWidthForRotationDefault[upsideDownRotation] =
@@ -2660,16 +2683,10 @@
mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset);
mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
- // EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
- mExperiments.onConfigurationChanged(uiContext);
- // EXPERIMENT END
-
- // EXPERIMENT: TODO(b/113952590): Replace with real code after experiment.
- // This should calculate how much above the frame we accept gestures. Currently,
- // we extend the frame to capture the gestures, so this is 0.
- mBottomGestureAdditionalInset = mExperiments.getNavigationBarFrameHeight()
- - mExperiments.getNavigationBarFrameHeight();
- // EXPERIMENT END
+ // This should calculate how much above the frame we accept gestures.
+ mBottomGestureAdditionalInset = Math.max(0,
+ res.getDimensionPixelSize(R.dimen.navigation_bar_gesture_height)
+ - getNavigationBarFrameHeight(portraitRotation, uiMode));
updateConfigurationAndScreenSizeDependentBehaviors();
mWindowOutsetBottom = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
@@ -2735,6 +2752,26 @@
}
/**
+ * 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/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 904c503..14585c5 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -274,7 +274,7 @@
}
// Even though we want to keep original bounds, we still don't want it to stomp on
// an existing task.
- adjustBoundsToAvoidConflict(display, outParams.mBounds);
+ adjustBoundsToAvoidConflictInDisplay(display, outParams.mBounds);
}
} else {
if (source != null && source.inFreeformWindowingMode()
@@ -534,7 +534,7 @@
}
// Lastly we adjust bounds to avoid conflicts with other tasks as much as possible.
- adjustBoundsToAvoidConflict(display, inOutBounds);
+ adjustBoundsToAvoidConflictInDisplay(display, inOutBounds);
}
private int convertOrientationToScreenOrientation(int orientation) {
@@ -678,16 +678,9 @@
* @param display the display which tasks are to check
* @param inOutBounds the bounds used to input initial bounds and output result bounds
*/
- private void adjustBoundsToAvoidConflict(@NonNull ActivityDisplay display,
+ private void adjustBoundsToAvoidConflictInDisplay(@NonNull ActivityDisplay display,
@NonNull Rect inOutBounds) {
- final Rect displayBounds = display.getBounds();
- if (!displayBounds.contains(inOutBounds)) {
- // The initial bounds are already out of display. The scanning algorithm below doesn't
- // work so well with them.
- return;
- }
-
- final List<TaskRecord> tasksToCheck = new ArrayList<>();
+ final List<Rect> taskBoundsToCheck = new ArrayList<>();
for (int i = 0; i < display.getChildCount(); ++i) {
final ActivityStack stack = display.getChildAt(i);
if (!stack.inFreeformWindowingMode()) {
@@ -695,11 +688,35 @@
}
for (int j = 0; j < stack.getChildCount(); ++j) {
- tasksToCheck.add(stack.getChildAt(j));
+ taskBoundsToCheck.add(stack.getChildAt(j).getBounds());
}
}
+ adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds);
+ }
- if (!boundsConflict(tasksToCheck, inOutBounds)) {
+ /**
+ * Adjusts input bounds to avoid conflict with provided display bounds and list of tasks bounds
+ * for the display.
+ *
+ * Scans the bounds in directions to find a candidate location that does not conflict with the
+ * provided list of task bounds. If starting bounds are outside the display bounds or if no
+ * suitable candidate bounds are found, the method returns the input bounds.
+ *
+ * @param displayBounds display bounds used to restrict the candidate bounds
+ * @param taskBoundsToCheck list of task bounds to check for conflict
+ * @param inOutBounds the bounds used to input initial bounds and output result bounds
+ */
+ @VisibleForTesting
+ void adjustBoundsToAvoidConflict(@NonNull Rect displayBounds,
+ @NonNull List<Rect> taskBoundsToCheck,
+ @NonNull Rect inOutBounds) {
+ if (!displayBounds.contains(inOutBounds)) {
+ // The initial bounds are already out of display. The scanning algorithm below doesn't
+ // work so well with them.
+ return;
+ }
+
+ if (!boundsConflict(taskBoundsToCheck, inOutBounds)) {
// Current proposal doesn't conflict with any task. Early return to avoid unnecessary
// calculation.
return;
@@ -713,11 +730,13 @@
}
mTmpBounds.set(inOutBounds);
- while (boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) {
+ while (boundsConflict(taskBoundsToCheck, mTmpBounds)
+ && displayBounds.contains(mTmpBounds)) {
shiftBounds(direction, displayBounds, mTmpBounds);
}
- if (!boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) {
+ if (!boundsConflict(taskBoundsToCheck, mTmpBounds)
+ && displayBounds.contains(mTmpBounds)) {
// Found a candidate. Just use this.
inOutBounds.set(mTmpBounds);
if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds);
@@ -772,16 +791,16 @@
mTmpDirections[1] = Gravity.TOP | Gravity.LEFT;
}
- private boolean boundsConflict(@NonNull List<TaskRecord> tasks, @NonNull Rect bounds) {
- for (TaskRecord task : tasks) {
- final Rect taskBounds = task.getBounds();
- final boolean leftClose = Math.abs(taskBounds.left - bounds.left)
+ private boolean boundsConflict(@NonNull List<Rect> taskBoundsToCheck,
+ @NonNull Rect candidateBounds) {
+ for (Rect taskBounds : taskBoundsToCheck) {
+ final boolean leftClose = Math.abs(taskBounds.left - candidateBounds.left)
< BOUNDS_CONFLICT_THRESHOLD;
- final boolean topClose = Math.abs(taskBounds.top - bounds.top)
+ final boolean topClose = Math.abs(taskBounds.top - candidateBounds.top)
< BOUNDS_CONFLICT_THRESHOLD;
- final boolean rightClose = Math.abs(taskBounds.right - bounds.right)
+ final boolean rightClose = Math.abs(taskBounds.right - candidateBounds.right)
< BOUNDS_CONFLICT_THRESHOLD;
- final boolean bottomClose = Math.abs(taskBounds.bottom - bounds.bottom)
+ final boolean bottomClose = Math.abs(taskBounds.bottom - candidateBounds.bottom)
< BOUNDS_CONFLICT_THRESHOLD;
if ((leftClose && topClose) || (leftClose && bottomClose) || (rightClose && topClose)
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index eb919eb..12b62b9 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -23,6 +23,7 @@
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -98,7 +99,7 @@
private final ActivityTaskManagerService mAtm;
// The actual proc... may be null only if 'persistent' is true (in which case we are in the
// process of launching the app)
- private volatile IApplicationThread mThread;
+ private IApplicationThread mThread;
// Currently desired scheduling class
private volatile int mCurSchedGroup;
// Currently computed process state
@@ -192,8 +193,11 @@
return mPid;
}
+ @HotPath(caller = HotPath.PROCESS_CHANGE)
public void setThread(IApplicationThread thread) {
- mThread = thread;
+ synchronized (mAtm.mGlobalLockWithoutBoost) {
+ mThread = thread;
+ }
}
IApplicationThread getThread() {
@@ -507,7 +511,14 @@
continue;
}
ActivityRecord topActivity = task.getTopActivity();
- if (topActivity != null && topActivity.visible) {
+ if (topActivity == null) {
+ continue;
+ }
+ // If an activity has just been started it will not yet be visible, but
+ // is expected to be soon. We treat this as if it were already visible.
+ // This ensures a subsequent activity can be started even before this one
+ // becomes visible.
+ if (topActivity.visible || topActivity.isState(INITIALIZING)) {
return true;
}
}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index f73a285..d72f1cf 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -3,8 +3,6 @@
name: "ipmemorystore-aidl-interfaces",
local_include_dir: "java",
srcs: [
- // TODO: Define and use a filegroup for these files, since they're also used in
- // networkstack-aidl-interfaces. This does not appear to work at the moment.
"java/android/net/IIpMemoryStore.aidl",
"java/android/net/IIpMemoryStoreCallbacks.aidl",
"java/android/net/ipmemorystore/**/*.aidl",
@@ -17,17 +15,16 @@
enabled: false,
},
},
- api_dir: "aidl/networkstack",
+ api_dir: "aidl/ipmemorystore",
+ versions: ["1"],
}
aidl_interface {
name: "networkstack-aidl-interfaces",
local_include_dir: "java",
- include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
+ include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
srcs: [
"java/android/net/DhcpResultsParcelable.aidl",
- "java/android/net/IIpMemoryStore.aidl",
- "java/android/net/IIpMemoryStoreCallbacks.aidl",
"java/android/net/INetworkMonitor.aidl",
"java/android/net/INetworkMonitorCallbacks.aidl",
"java/android/net/INetworkStackConnector.aidl",
@@ -41,7 +38,6 @@
"java/android/net/dhcp/IDhcpServerCallbacks.aidl",
"java/android/net/ip/IIpClient.aidl",
"java/android/net/ip/IIpClientCallbacks.aidl",
- "java/android/net/ipmemorystore/**/*.aidl",
],
backend: {
ndk: {
@@ -52,6 +48,8 @@
},
},
api_dir: "aidl/networkstack",
+ imports: ["ipmemorystore-aidl-interfaces"],
+ versions: ["1"],
}
java_library_static {
@@ -62,7 +60,7 @@
"ipmemorystore-client",
"netd_aidl_interface-java",
"networkstack-aidl-interfaces-java",
- ]
+ ],
}
java_library_static {
@@ -75,7 +73,7 @@
],
static_libs: [
"ipmemorystore-aidl-interfaces-java",
- ]
+ ],
}
filegroup {
diff --git a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl
new file mode 100644
index 0000000..a8cbab2
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl
@@ -0,0 +1,9 @@
+package android.net;
+interface IIpMemoryStore {
+ oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener);
+ oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener);
+ oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener);
+ oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener);
+ oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener);
+ oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl
new file mode 100644
index 0000000..cf02c26
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl
@@ -0,0 +1,4 @@
+package android.net;
+interface IIpMemoryStoreCallbacks {
+ oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl
new file mode 100644
index 0000000..291dbef
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+parcelable Blob {
+ byte[] data;
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
new file mode 100644
index 0000000..52f40d4
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnBlobRetrievedListener {
+ oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
new file mode 100644
index 0000000..7853514
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnL2KeyResponseListener {
+ oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
new file mode 100644
index 0000000..3dd2ae6
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnNetworkAttributesRetrievedListener {
+ oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
new file mode 100644
index 0000000..46d4ecb
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnSameL3NetworkResponseListener {
+ oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl
new file mode 100644
index 0000000..54e654b
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnStatusListener {
+ oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status);
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
new file mode 100644
index 0000000..9531ea3
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -0,0 +1,8 @@
+package android.net.ipmemorystore;
+parcelable NetworkAttributesParcelable {
+ byte[] assignedV4Address;
+ long assignedV4AddressExpiry;
+ String groupHint;
+ android.net.ipmemorystore.Blob[] dnsAddresses;
+ int mtu;
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
new file mode 100644
index 0000000..414272b
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
@@ -0,0 +1,6 @@
+package android.net.ipmemorystore;
+parcelable SameL3NetworkResponseParcelable {
+ String l2Key1;
+ String l2Key2;
+ float confidence;
+}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl
new file mode 100644
index 0000000..92c6779
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+parcelable StatusParcelable {
+ int resultCode;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl
new file mode 100644
index 0000000..92b5345
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,8 @@
+package android.net;
+parcelable DhcpResultsParcelable {
+ android.net.StaticIpConfiguration baseConfiguration;
+ int leaseDuration;
+ int mtu;
+ String serverAddress;
+ String vendorInfo;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl
new file mode 100644
index 0000000..b19f522
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl
@@ -0,0 +1,17 @@
+package android.net;
+interface INetworkMonitor {
+ oneway void start();
+ oneway void launchCaptivePortalApp();
+ oneway void notifyCaptivePortalAppFinished(int response);
+ oneway void setAcceptPartialConnectivity();
+ oneway void forceReevaluation(int uid);
+ oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
+ oneway void notifyDnsResponse(int returnCode);
+ oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
+ oneway void notifyNetworkDisconnected();
+ oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
+ oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
+ const int NETWORK_TEST_RESULT_VALID = 0;
+ const int NETWORK_TEST_RESULT_INVALID = 1;
+ const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl
new file mode 100644
index 0000000..ee9871d
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,8 @@
+package android.net;
+interface INetworkMonitorCallbacks {
+ oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor);
+ oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl);
+ oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config);
+ oneway void showProvisioningNotification(String action, String packageName);
+ oneway void hideProvisioningNotification();
+}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl
new file mode 100644
index 0000000..7da11e4
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,7 @@
+package android.net;
+interface INetworkStackConnector {
+ oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
+ oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
+ oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
+ oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
+}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..f6ca6f7
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,4 @@
+package android.net;
+interface INetworkStackStatusCallback {
+ oneway void onStatusAvailable(int statusCode);
+}
diff --git a/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl
new file mode 100644
index 0000000..c80a787
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,7 @@
+package android.net;
+parcelable InitialConfigurationParcelable {
+ android.net.LinkAddress[] ipAddresses;
+ android.net.IpPrefix[] directlyConnectedRoutes;
+ String[] dnsServers;
+ String gateway;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl
new file mode 100644
index 0000000..2de790b
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,5 @@
+package android.net;
+parcelable PrivateDnsConfigParcel {
+ String hostname;
+ String[] ips;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl
new file mode 100644
index 0000000..3a6c304
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,15 @@
+package android.net;
+parcelable ProvisioningConfigurationParcelable {
+ boolean enableIPv4;
+ boolean enableIPv6;
+ boolean usingMultinetworkPolicyTracker;
+ boolean usingIpReachabilityMonitor;
+ int requestedPreDhcpActionMs;
+ android.net.InitialConfigurationParcelable initialConfig;
+ android.net.StaticIpConfiguration staticIpConfig;
+ android.net.apf.ApfCapabilities apfCapabilities;
+ int provisioningTimeoutMs;
+ int ipv6AddrGenMode;
+ android.net.Network network;
+ String displayName;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..e121c06
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,13 @@
+package android.net;
+parcelable TcpKeepalivePacketDataParcelable {
+ byte[] srcAddress;
+ int srcPort;
+ byte[] dstAddress;
+ int dstPort;
+ int seq;
+ int ack;
+ int rcvWnd;
+ int rcvWndScale;
+ int tos;
+ int ttl;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..67193ae
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,11 @@
+package android.net.dhcp;
+parcelable DhcpServingParamsParcel {
+ int serverAddr;
+ int serverAddrPrefixLength;
+ int[] defaultRouters;
+ int[] dnsServers;
+ int[] excludedAddrs;
+ long dhcpLeaseTimeSecs;
+ int linkMtu;
+ boolean metered;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..9143158
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,10 @@
+package android.net.dhcp;
+interface IDhcpServer {
+ oneway void start(in android.net.INetworkStackStatusCallback cb);
+ oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb);
+ oneway void stop(in android.net.INetworkStackStatusCallback cb);
+ const int STATUS_UNKNOWN = 0;
+ const int STATUS_SUCCESS = 1;
+ const int STATUS_INVALID_ARGUMENT = 2;
+ const int STATUS_UNKNOWN_ERROR = 3;
+}
diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..dcc4489
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,4 @@
+package android.net.dhcp;
+interface IDhcpServerCallbacks {
+ oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
+}
diff --git a/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl
new file mode 100644
index 0000000..95a1574
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl
@@ -0,0 +1,14 @@
+package android.net.ip;
+interface IIpClient {
+ oneway void completedPreDhcpAction();
+ oneway void confirmConfiguration();
+ oneway void readPacketFilterComplete(in byte[] data);
+ oneway void shutdown();
+ oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
+ oneway void stop();
+ oneway void setTcpBufferSizes(in String tcpBufferSizes);
+ oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
+ oneway void setMulticastFilter(boolean enabled);
+ oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
+ oneway void removeKeepalivePacketFilter(int slot);
+}
diff --git a/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl
new file mode 100644
index 0000000..d6bc808
--- /dev/null
+++ b/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,16 @@
+package android.net.ip;
+interface IIpClientCallbacks {
+ oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
+ oneway void onPreDhcpAction();
+ oneway void onPostDhcpAction();
+ oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
+ oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
+ oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
+ oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
+ oneway void onReachabilityLost(in String logMsg);
+ oneway void onQuit();
+ oneway void installPacketFilter(in byte[] filter);
+ oneway void startReadPacketFilter();
+ oneway void setFallbackMulticastFilter(boolean enabled);
+ oneway void setNeighborDiscoveryOffload(boolean enable);
+}
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
index 9248299..4a115e6 100644
--- a/services/net/java/android/net/IpMemoryStore.java
+++ b/services/net/java/android/net/IpMemoryStore.java
@@ -41,6 +41,11 @@
public void onIpMemoryStoreFetched(final IIpMemoryStore memoryStore) {
mService.complete(memoryStore);
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
});
}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 90624e0..714ade1 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -175,6 +175,11 @@
public void setNeighborDiscoveryOffload(boolean enable) {
mCb.setNeighborDiscoveryOffload(enable);
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
/**
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index fc1128b..66884c6 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -277,6 +277,11 @@
}
public abstract void callback(int statusCode);
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
}
private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
diff --git a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
index 22978a2..a17483a 100644
--- a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
+++ b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
@@ -40,6 +40,11 @@
listener.onBlobRetrieved(new Status(statusParcelable), l2Key, name, blob);
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
}
diff --git a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
index 9e7c1c8..e608aec 100644
--- a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
+++ b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
@@ -40,6 +40,11 @@
listener.onL2KeyResponse(new Status(statusParcelable), l2Key);
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
}
diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
index 59da268..ca6f302 100644
--- a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
+++ b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
@@ -44,6 +44,11 @@
new NetworkAttributes(networkAttributesParcelable));
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
}
diff --git a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
index 0154fd2..67f8da8 100644
--- a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
+++ b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
@@ -43,6 +43,11 @@
new SameL3NetworkResponse(sameL3NetworkResponseParcelable));
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
}
diff --git a/services/net/java/android/net/ipmemorystore/OnStatusListener.java b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
index 824b7b0..4262efd 100644
--- a/services/net/java/android/net/ipmemorystore/OnStatusListener.java
+++ b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
@@ -39,6 +39,11 @@
listener.onComplete(new Status(statusParcelable));
}
}
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
};
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 08f6a37..f492d13 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -58,6 +58,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -224,50 +225,55 @@
}
private void setProcessState(int procState) {
+ setProcessState(procState, mSourceUid);
+ }
+
+ private void setProcessState(int procState, int uid) {
try {
- doReturn(procState).when(mActivityMangerInternal).getUidProcessState(mSourceUid);
+ doReturn(procState).when(mActivityMangerInternal).getUidProcessState(uid);
SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids();
spyOn(foregroundUids);
- mUidObserver.onUidStateChanged(mSourceUid, procState, 0);
+ mUidObserver.onUidStateChanged(uid, procState, 0);
if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1))
- .put(eq(mSourceUid), eq(true));
- assertTrue(foregroundUids.get(mSourceUid));
+ verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1))
+ .put(eq(uid), eq(true));
+ assertTrue(foregroundUids.get(uid));
} else {
- verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)).delete(eq(mSourceUid));
- assertFalse(foregroundUids.get(mSourceUid));
+ verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid));
+ assertFalse(foregroundUids.get(uid));
}
} catch (RemoteException e) {
fail("registerUidObserver threw exception: " + e.getMessage());
}
}
- private void setStandbyBucket(int bucketIndex) {
- int bucket;
+ private int bucketIndexToUsageStatsBucket(int bucketIndex) {
switch (bucketIndex) {
case ACTIVE_INDEX:
- bucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
- break;
+ return UsageStatsManager.STANDBY_BUCKET_ACTIVE;
case WORKING_INDEX:
- bucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
- break;
+ return UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
case FREQUENT_INDEX:
- bucket = UsageStatsManager.STANDBY_BUCKET_FREQUENT;
- break;
+ return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
case RARE_INDEX:
- bucket = UsageStatsManager.STANDBY_BUCKET_RARE;
- break;
+ return UsageStatsManager.STANDBY_BUCKET_RARE;
default:
- bucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
+ return UsageStatsManager.STANDBY_BUCKET_NEVER;
}
+ }
+
+ private void setStandbyBucket(int bucketIndex) {
when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID),
- anyLong())).thenReturn(bucket);
+ anyLong())).thenReturn(bucketIndexToUsageStatsBucket(bucketIndex));
}
private void setStandbyBucket(int bucketIndex, JobStatus... jobs) {
setStandbyBucket(bucketIndex);
for (JobStatus job : jobs) {
job.setStandbyBucket(bucketIndex);
+ when(mUsageStatsManager.getAppStandbyBucket(
+ eq(job.getSourcePackageName()), eq(job.getSourceUserId()), anyLong()))
+ .thenReturn(bucketIndexToUsageStatsBucket(bucketIndex));
}
}
@@ -283,8 +289,13 @@
new ComponentName(mContext, "TestQuotaJobService"))
.setMinimumLatency(Math.abs(jobId) + 1)
.build();
+ return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo);
+ }
+
+ private JobStatus createJobStatus(String testTag, String packageName, int callingUid,
+ JobInfo jobInfo) {
JobStatus js = JobStatus.createFromJobInfo(
- jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
// Make sure tests aren't passing just because the default bucket is likely ACTIVE.
js.setStandbyBucket(FREQUENT_INDEX);
return js;
@@ -935,6 +946,115 @@
}
@Test
+ public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS() {
+ setDischarging();
+
+ JobStatus jobStatus = createJobStatus(
+ "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_BelowFGS", 1);
+ setStandbyBucket(ACTIVE_INDEX, jobStatus);
+ setProcessState(ActivityManager.PROCESS_STATE_BACKUP);
+
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ for (int i = 0; i < 20; ++i) {
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ }
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+
+ advanceElapsedClock(15 * SECOND_IN_MILLIS);
+
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ for (int i = 0; i < 20; ++i) {
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ }
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+
+ advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS);
+
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInAllowedTime);
+ assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus));
+ }
+
+ @Test
+ public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps()
+ throws Exception {
+ setDischarging();
+
+ final String unaffectedPkgName = "com.android.unaffected";
+ final int unaffectedUid = 10987;
+ JobInfo unaffectedJobInfo = new JobInfo.Builder(1,
+ new ComponentName(unaffectedPkgName, "foo"))
+ .build();
+ JobStatus unaffected = createJobStatus(
+ "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps",
+ unaffectedPkgName, unaffectedUid, unaffectedJobInfo);
+ setStandbyBucket(FREQUENT_INDEX, unaffected);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE, unaffectedUid);
+
+ final String fgChangerPkgName = "com.android.foreground.changer";
+ final int fgChangerUid = 10234;
+ JobInfo fgChangerJobInfo = new JobInfo.Builder(2,
+ new ComponentName(fgChangerPkgName, "foo"))
+ .build();
+ JobStatus fgStateChanger = createJobStatus(
+ "testIsWithinQuotaLocked_UnderDuration_UnderJobCount_MultiStateChange_SeparateApps",
+ fgChangerPkgName, fgChangerUid, fgChangerJobInfo);
+ setStandbyBucket(ACTIVE_INDEX, fgStateChanger);
+ setProcessState(ActivityManager.PROCESS_STATE_BACKUP, fgChangerUid);
+
+ IPackageManager packageManager = AppGlobals.getPackageManager();
+ spyOn(packageManager);
+ doReturn(new String[]{unaffectedPkgName})
+ .when(packageManager).getPackagesForUid(unaffectedUid);
+ doReturn(new String[]{fgChangerPkgName})
+ .when(packageManager).getPackagesForUid(fgChangerUid);
+
+ mQuotaController.maybeStartTrackingJobLocked(unaffected, null);
+ mQuotaController.prepareForExecutionLocked(unaffected);
+
+ mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null);
+ mQuotaController.prepareForExecutionLocked(fgStateChanger);
+ for (int i = 0; i < 20; ++i) {
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid);
+ }
+ mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+
+ advanceElapsedClock(15 * SECOND_IN_MILLIS);
+
+ mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null);
+ mQuotaController.prepareForExecutionLocked(fgStateChanger);
+ for (int i = 0; i < 20; ++i) {
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid);
+ }
+ mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+
+ mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false);
+
+ assertTrue(mQuotaController.isWithinQuotaLocked(unaffected));
+ assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger));
+ assertEquals(1,
+ mQuotaController.getTimingSessions(SOURCE_USER_ID, unaffectedPkgName).size());
+ assertEquals(42,
+ mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size());
+ for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) {
+ assertEquals(42, mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, fgChangerPkgName, i).jobCountInAllowedTime);
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, unaffectedPkgName, i).jobCountInAllowedTime);
+ }
+ }
+
+ @Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
mQuotaController.maybeScheduleCleanupAlarmLocked();
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 4de00f7..c30a7dd 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -22,6 +22,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -216,6 +218,53 @@
}
@Test
+ public void testCallbackOnSuccess_doesNotCallNonCurrentCallback() {
+ mAttentionDetector.mRequestId = 5;
+ registerAttention(); // mRequestId = 6;
+ mAttentionDetector.mRequestId = 55;
+
+ mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT,
+ SystemClock.uptimeMillis());
+ verify(mOnUserAttention, never()).run();
+ }
+
+ @Test
+ public void testCallbackOnSuccess_callsCallbackAfterOldCallbackCame() {
+ mAttentionDetector.mRequestId = 5;
+ registerAttention(); // mRequestId = 6;
+ mAttentionDetector.mRequestId = 55;
+
+ mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT,
+ SystemClock.uptimeMillis()); // old callback came
+ mAttentionDetector.mRequestId = 6; // now back to current
+ mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT,
+ SystemClock.uptimeMillis());
+ verify(mOnUserAttention).run();
+ }
+
+ @Test
+ public void testCallbackOnSuccess_DoesNotGoIntoInfiniteLoop() {
+ // Mimic real behavior
+ doAnswer((invocation) -> {
+ // Mimic a cache hit: calling onSuccess() immediately
+ registerAttention();
+ mAttentionDetector.mRequestId++;
+ mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT,
+ SystemClock.uptimeMillis());
+ return null;
+ }).when(mOnUserAttention).run();
+
+ registerAttention();
+ // This test fails with literal stack overflow:
+ // e.g. java.lang.StackOverflowError: stack size 1039KB
+ mAttentionDetector.mCallback.onSuccess(AttentionService.ATTENTION_SUCCESS_PRESENT,
+ SystemClock.uptimeMillis());
+
+ // We don't actually get here when the test fails
+ verify(mOnUserAttention, atMost(1)).run();
+ }
+
+ @Test
public void testCallbackOnFailure_unregistersCurrentRequestCode() {
registerAttention();
mAttentionDetector.mCallback.onFailure(AttentionService.ATTENTION_FAILURE_UNKNOWN);
@@ -232,7 +281,6 @@
}
private class TestableAttentionDetector extends AttentionDetector {
-
private boolean mAttentionServiceSupported;
TestableAttentionDetector() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 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/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b34bd25..1a06490 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -1644,6 +1644,38 @@
}
@Test
+ public void testClearData() {
+ ArraySet<String> pkg = new ArraySet<>();
+ pkg.add(PKG_O);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+ mHelper.createNotificationChannelGroup(
+ PKG_O, UID_O, new NotificationChannelGroup("1", "bye"), true);
+ mHelper.lockChannelsForOEM(pkg.toArray(new String[]{}));
+ mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, pkg);
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "", 1);
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
+ mHelper.setBubblesAllowed(PKG_O, UID_O, false);
+ mHelper.setShowBadge(PKG_O, UID_O, false);
+ mHelper.setAppImportanceLocked(PKG_O, UID_O);
+
+ mHelper.clearData(PKG_O, UID_O);
+
+ assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O, UID_O));
+ assertTrue(mHelper.areBubblesAllowed(PKG_O, UID_O));
+ assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size());
+
+ NotificationChannel channel = getChannel();
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ assertTrue(channel.isImportanceLockedByCriticalDeviceFunction());
+ assertTrue(channel.isImportanceLockedByOEM());
+ }
+
+ @Test
public void testRecordDefaults() throws Exception {
assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
UID_N_MR1));
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index bfda2ea..5136705 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -42,6 +42,7 @@
android:testOnly="true"
android:largeHeap="true">
<uses-library android:name="android.test.mock" android:required="true" />
+ <uses-library android:name="android.test.runner" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 5b32fe6..292a05b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.when;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -78,7 +79,9 @@
@Override
public SurfaceControl build() {
- return mock(SurfaceControl.class);
+ SurfaceControl mSc = mock(SurfaceControl.class);
+ when(mSc.isValid()).thenReturn(true);
+ return mSc;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 07dd93c..1684f97 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -22,13 +22,17 @@
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -38,6 +42,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
@@ -232,4 +237,40 @@
displayRotation, Surface.ROTATION_0, Surface.ROTATION_90));
}
}
+
+ @Test
+ public void testShouldShowToastWhenScreenLocked() {
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ final WindowState activity = createApplicationWindow();
+ final WindowState toast = createToastWindow();
+
+ synchronized (mWm.mGlobalLock) {
+ policy.adjustWindowParamsLw(
+ toast, toast.mAttrs, 0 /* callingPid */, 0 /* callingUid */);
+
+ assertTrue(policy.canToastShowWhenLocked(0 /* callingUid */));
+ assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED);
+ }
+ }
+
+ private WindowState createToastWindow() {
+ final WindowState win = createWindow(null, TYPE_TOAST, "Toast");
+ final WindowManager.LayoutParams attrs = win.mAttrs;
+ attrs.width = WRAP_CONTENT;
+ attrs.height = WRAP_CONTENT;
+ attrs.flags = FLAG_KEEP_SCREEN_ON | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE;
+ attrs.format = PixelFormat.TRANSLUCENT;
+ return win;
+ }
+
+ private WindowState createApplicationWindow() {
+ final WindowState win = createWindow(null, TYPE_APPLICATION, "Application");
+ final WindowManager.LayoutParams attrs = win.mAttrs;
+ attrs.width = MATCH_PARENT;
+ attrs.height = MATCH_PARENT;
+ attrs.flags = FLAG_SHOW_WHEN_LOCKED | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ attrs.format = PixelFormat.OPAQUE;
+ win.mHasSurface = true;
+ return win;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 4e906bc..4673992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -39,7 +39,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Predicate;
/**
* Build/Install/Run:
@@ -47,208 +46,222 @@
*/
@MediumTest
@Presubmit
-public class PersisterQueueTests implements PersisterQueue.Listener {
+public class PersisterQueueTests {
private static final long INTER_WRITE_DELAY_MS = 50;
private static final long PRE_TASK_DELAY_MS = 300;
- // We allow at most 1s more than the expected timeout.
- private static final long TIMEOUT_ALLOWANCE = 100;
-
- private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
-
- private AtomicInteger mItemCount;
- private CountDownLatch mSetUpLatch;
- private volatile CountDownLatch mLatch;
- private List<Boolean> mProbablyDoneResults;
+ // We allow at most 0.2s more than the expected timeout.
+ private static final long TIMEOUT_ALLOWANCE = 200;
private final PersisterQueue mTarget =
new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+ private TestPersisterQueueListener mListener;
+ private TestWriteQueueItemFactory mFactory;
+
@Before
public void setUp() throws Exception {
- mItemCount = new AtomicInteger(0);
- mProbablyDoneResults = new ArrayList<>();
- mSetUpLatch = new CountDownLatch(1);
+ mListener = new TestPersisterQueueListener();
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
+ mTarget.addListener(mListener);
- mTarget.addListener(this);
+ mFactory = new TestWriteQueueItemFactory();
+
mTarget.startPersisting();
assertTrue("Target didn't call callback on start up.",
- mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
}
@After
public void tearDown() throws Exception {
mTarget.stopPersisting();
- mTarget.removeListener(this);
+ mTarget.removeListener(mListener);
}
@Test
public void testCallCallbackOnStartUp() {
// The onPreProcessItem() must be called on start up.
- assertEquals(1, mProbablyDoneResults.size());
+ assertEquals(1, mListener.mProbablyDoneResults.size());
// The last one must be called with probably done being true.
- assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
+ assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(0));
}
@Test
public void testProcessOneItem() throws Exception {
- mLatch = new CountDownLatch(1);
+ mFactory.setExpectedProcessedItemNumber(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
final long dispatchTime = SystemClock.uptimeMillis();
- mTarget.addItem(new TestItem(), false);
- assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ mTarget.addItem(mFactory.createItem(), false);
+ assertTrue("Target didn't process item enough times.",
+ mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
assertTrue("Target didn't wait enough time before processing item. duration: "
+ processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
processDuration >= PRE_TASK_DELAY_MS);
+ assertTrue("Target didn't call callback enough times.",
+ mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
// Once before processing this item, once after that.
- assertEquals(2, mProbablyDoneResults.size());
+ assertEquals(2, mListener.mProbablyDoneResults.size());
// The last one must be called with probably done being true.
- assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+ assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(1));
}
@Test
public void testProcessOneItem_Flush() throws Exception {
- mLatch = new CountDownLatch(1);
+ mFactory.setExpectedProcessedItemNumber(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
final long dispatchTime = SystemClock.uptimeMillis();
- mTarget.addItem(new TestItem(), true);
- assertTrue("Target didn't call callback enough times.",
- mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ mTarget.addItem(mFactory.createItem(), true);
+ assertTrue("Target didn't process item enough times.",
+ mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
assertTrue("Target didn't process item immediately when flushing. duration: "
+ processDuration + "ms pretask delay: "
+ PRE_TASK_DELAY_MS + "ms",
processDuration < PRE_TASK_DELAY_MS);
+ assertTrue("Target didn't call callback enough times.",
+ mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE));
// Once before processing this item, once after that.
- assertEquals(2, mProbablyDoneResults.size());
+ assertEquals(2, mListener.mProbablyDoneResults.size());
// The last one must be called with probably done being true.
- assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+ assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(1));
}
@Test
public void testProcessTwoItems() throws Exception {
- mLatch = new CountDownLatch(2);
+ mFactory.setExpectedProcessedItemNumber(2);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(2);
final long dispatchTime = SystemClock.uptimeMillis();
- mTarget.addItem(new TestItem(), false);
- mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(mFactory.createItem(), false);
+ mTarget.addItem(mFactory.createItem(), false);
assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
- TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process all items.", 2, mItemCount.get());
+ mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS
+ + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process all items.", 2, mFactory.getTotalProcessedItemCount());
final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
assertTrue("Target didn't wait enough time before processing item. duration: "
+ processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
+ "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
-
+ assertTrue("Target didn't call the onPreProcess callback enough times",
+ mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
// Once before processing this item, once after that.
- assertEquals(3, mProbablyDoneResults.size());
+ assertEquals(3, mListener.mProbablyDoneResults.size());
// The first one must be called with probably done being false.
- assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
+ assertFalse("The first probablyDone must be false.", mListener.mProbablyDoneResults.get(1));
// The last one must be called with probably done being true.
- assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+ assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(2));
}
@Test
@FlakyTest(bugId = 128526085)
public void testProcessTwoItems_OneAfterAnother() throws Exception {
// First item
- mLatch = new CountDownLatch(1);
+ mFactory.setExpectedProcessedItemNumber(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
long dispatchTime = SystemClock.uptimeMillis();
- mTarget.addItem(new TestItem(), false);
- assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+ mTarget.addItem(mFactory.createItem(), false);
+ assertTrue("Target didn't process item enough times.",
+ mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
long processDuration = SystemClock.uptimeMillis() - dispatchTime;
assertTrue("Target didn't wait enough time before processing item."
+ processDuration + "ms pretask delay: "
+ PRE_TASK_DELAY_MS + "ms",
processDuration >= PRE_TASK_DELAY_MS);
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
+ assertTrue("Target didn't call callback enough times.",
+ mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
// Second item
- mLatch = new CountDownLatch(1);
+ mFactory.setExpectedProcessedItemNumber(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
dispatchTime = SystemClock.uptimeMillis();
// Synchronize on the instance to make sure we schedule the item after it starts to wait for
// task indefinitely.
synchronized (mTarget) {
- mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(mFactory.createItem(), false);
}
- assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process all items.", 2, mItemCount.get());
+ assertTrue("Target didn't process item enough times.",
+ mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process all items.", 2, mFactory.getTotalProcessedItemCount());
processDuration = SystemClock.uptimeMillis() - dispatchTime;
assertTrue("Target didn't wait enough time before processing item. Process time: "
+ processDuration + "ms pre task delay: "
+ PRE_TASK_DELAY_MS + "ms",
processDuration >= PRE_TASK_DELAY_MS);
+ assertTrue("Target didn't call callback enough times.",
+ mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
// Once before processing this item, once after that.
- assertEquals(3, mProbablyDoneResults.size());
+ assertEquals(3, mListener.mProbablyDoneResults.size());
// The last one must be called with probably done being true.
- assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+ assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(2));
}
@Test
public void testFindLastItemNotReturnDifferentType() {
synchronized (mTarget) {
- mTarget.addItem(new TestItem(), false);
- assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ mTarget.addItem(mFactory.createItem(), false);
+ assertNull(mTarget.findLastItem(TestItem::shouldKeepOnFilter,
+ FilterableTestItem.class));
}
}
@Test
public void testFindLastItemNotReturnMismatchItem() {
synchronized (mTarget) {
- mTarget.addItem(new MatchingTestItem(false), false);
- assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ mTarget.addItem(mFactory.createFilterableItem(false), false);
+ assertNull(mTarget.findLastItem(TestItem::shouldKeepOnFilter,
+ FilterableTestItem.class));
}
}
@Test
public void testFindLastItemReturnMatchedItem() {
synchronized (mTarget) {
- final MatchingTestItem item = new MatchingTestItem(true);
+ final FilterableTestItem item = mFactory.createFilterableItem(true);
mTarget.addItem(item, false);
- assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+ assertSame(item, mTarget.findLastItem(TestItem::shouldKeepOnFilter,
+ FilterableTestItem.class));
}
}
@Test
public void testRemoveItemsNotRemoveDifferentType() throws Exception {
- mLatch = new CountDownLatch(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
synchronized (mTarget) {
- mTarget.addItem(new TestItem(), false);
- mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ mTarget.addItem(mFactory.createItem(), false);
+ mTarget.removeItems(TestItem::shouldKeepOnFilter, FilterableTestItem.class);
}
assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
}
@Test
public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
- mLatch = new CountDownLatch(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
synchronized (mTarget) {
- mTarget.addItem(new MatchingTestItem(false), false);
- mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ mTarget.addItem(mFactory.createFilterableItem(false), false);
+ mTarget.removeItems(TestItem::shouldKeepOnFilter, FilterableTestItem.class);
}
assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
}
@Test
public void testUpdateLastOrAddItemUpdatesMatchedItem() throws Exception {
- mLatch = new CountDownLatch(1);
- final MatchingTestItem scheduledItem = new MatchingTestItem(true);
- final MatchingTestItem expected = new MatchingTestItem(true);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
+ final FilterableTestItem scheduledItem = mFactory.createFilterableItem(true);
+ final FilterableTestItem expected = mFactory.createFilterableItem(true);
synchronized (mTarget) {
mTarget.addItem(scheduledItem, false);
mTarget.updateLastOrAddItem(expected, false);
@@ -256,15 +269,15 @@
assertSame(expected, scheduledItem.mUpdateFromItem);
assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
}
@Test
public void testUpdateLastOrAddItemUpdatesAddItemWhenNoMatch() throws Exception {
- mLatch = new CountDownLatch(2);
- final MatchingTestItem scheduledItem = new MatchingTestItem(false);
- final MatchingTestItem expected = new MatchingTestItem(true);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(2);
+ final FilterableTestItem scheduledItem = mFactory.createFilterableItem(false);
+ final FilterableTestItem expected = mFactory.createFilterableItem(true);
synchronized (mTarget) {
mTarget.addItem(scheduledItem, false);
mTarget.updateLastOrAddItem(expected, false);
@@ -272,73 +285,132 @@
assertNull(scheduledItem.mUpdateFromItem);
assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
- TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 2, mItemCount.get());
+ mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS
+ + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 2, mFactory.getTotalProcessedItemCount());
}
@Test
public void testRemoveItemsRemoveMatchedItem() throws Exception {
- mLatch = new CountDownLatch(1);
+ mListener.setExpectedOnPreProcessItemCallbackTimes(1);
synchronized (mTarget) {
- mTarget.addItem(new TestItem(), false);
- mTarget.addItem(new MatchingTestItem(true), false);
- mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+ mTarget.addItem(mFactory.createItem(), false);
+ mTarget.addItem(mFactory.createFilterableItem(true), false);
+ mTarget.removeItems(TestItem::shouldKeepOnFilter, FilterableTestItem.class);
}
assertTrue("Target didn't call callback enough times.",
- mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
- assertEquals("Target didn't process item.", 1, mItemCount.get());
+ mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+ assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
}
@Test
public void testFlushWaitSynchronously() {
final long dispatchTime = SystemClock.uptimeMillis();
- mTarget.addItem(new TestItem(), false);
- mTarget.addItem(new TestItem(), false);
+ mTarget.addItem(mFactory.createItem(), false);
+ mTarget.addItem(mFactory.createItem(), false);
mTarget.flush();
assertEquals("Flush should wait until all items are processed before return.",
- 2, mItemCount.get());
+ 2, mFactory.getTotalProcessedItemCount());
final long processTime = SystemClock.uptimeMillis() - dispatchTime;
assertWithMessage("Flush should trigger immediate flush without delays. processTime: "
+ processTime).that(processTime).isLessThan(TIMEOUT_ALLOWANCE);
}
- @Override
- public void onPreProcessItem(boolean queueEmpty) {
- mProbablyDoneResults.add(queueEmpty);
+ private static class TestWriteQueueItemFactory {
+ private final AtomicInteger mItemCount = new AtomicInteger(0);;
+ private CountDownLatch mLatch;
- final CountDownLatch latch = mLatch;
- if (latch != null) {
- latch.countDown();
+ int getTotalProcessedItemCount() {
+ return mItemCount.get();
}
- mSetUpLatch.countDown();
+ void setExpectedProcessedItemNumber(int countDown) {
+ mLatch = new CountDownLatch(countDown);
+ }
+
+ boolean waitForAllExpectedItemsProcessed(long timeoutInMilliseconds)
+ throws InterruptedException {
+ return mLatch.await(timeoutInMilliseconds, TimeUnit.MILLISECONDS);
+ }
+
+ TestItem createItem() {
+ return new TestItem(mItemCount, mLatch);
+ }
+
+ FilterableTestItem createFilterableItem(boolean shouldKeepOnFilter) {
+ return new FilterableTestItem(shouldKeepOnFilter, mItemCount, mLatch);
+ }
}
- private class TestItem<T extends TestItem<T>> implements PersisterQueue.WriteQueueItem<T> {
+ private static class TestItem<T extends TestItem<T>>
+ implements PersisterQueue.WriteQueueItem<T> {
+ private AtomicInteger mItemCount;
+ private CountDownLatch mLatch;
+
+ TestItem(AtomicInteger itemCount, CountDownLatch latch) {
+ mItemCount = itemCount;
+ mLatch = latch;
+ }
+
@Override
public void process() {
mItemCount.getAndIncrement();
+ if (mLatch != null) {
+ // Count down the latch at the last step is necessary, as it's a kind of lock to the
+ // next assert in many test cases.
+ mLatch.countDown();
+ }
+ }
+
+ boolean shouldKeepOnFilter() {
+ return true;
}
}
- private class MatchingTestItem extends TestItem<MatchingTestItem> {
- private boolean mMatching;
+ private static class FilterableTestItem extends TestItem<FilterableTestItem> {
+ private boolean mShouldKeepOnFilter;
- private MatchingTestItem mUpdateFromItem;
+ private FilterableTestItem mUpdateFromItem;
- private MatchingTestItem(boolean matching) {
- mMatching = matching;
+ private FilterableTestItem(boolean shouldKeepOnFilter, AtomicInteger mItemCount,
+ CountDownLatch mLatch) {
+ super(mItemCount, mLatch);
+ mShouldKeepOnFilter = shouldKeepOnFilter;
}
@Override
- public boolean matches(MatchingTestItem item) {
- return item.mMatching;
+ public boolean matches(FilterableTestItem item) {
+ return item.mShouldKeepOnFilter;
}
@Override
- public void updateFrom(MatchingTestItem item) {
+ public void updateFrom(FilterableTestItem item) {
mUpdateFromItem = item;
}
+
+ @Override
+ boolean shouldKeepOnFilter() {
+ return mShouldKeepOnFilter;
+ }
+ }
+
+ private class TestPersisterQueueListener implements PersisterQueue.Listener {
+ CountDownLatch mCallbackLatch;
+ final List<Boolean> mProbablyDoneResults = new ArrayList<>();
+
+ @Override
+ public void onPreProcessItem(boolean queueEmpty) {
+ mProbablyDoneResults.add(queueEmpty);
+ mCallbackLatch.countDown();
+ }
+
+ void setExpectedOnPreProcessItemCallbackTimes(int countDown) {
+ mCallbackLatch = new CountDownLatch(countDown);
+ }
+
+ boolean waitForAllExpectedCallbackDone(long timeoutInMilliseconds)
+ throws InterruptedException {
+ return mCallbackLatch.await(timeoutInMilliseconds, TimeUnit.MILLISECONDS);
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index cdbb121..f918149 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -52,6 +52,8 @@
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
/**
@@ -1206,6 +1208,23 @@
assertEquals(new Rect(120, 0, 320, 100), mResult.mBounds);
}
+ @Test
+ public void testAdjustBoundsToAvoidConflictAlwaysExits() {
+ Rect displayBounds = new Rect(0, 0, 40, 40);
+ List<Rect> existingTaskBounds = new ArrayList<>();
+ for (int i = 0; i < 9; i++) {
+ for (int j = 0; j < 9; j++) {
+ int left = i * 5;
+ int top = j * 5;
+ existingTaskBounds.add(new Rect(left, top, left + 20, top + 20));
+ }
+ }
+ Rect startingBounds = new Rect(0, 0, 20, 20);
+ Rect adjustedBounds = new Rect(startingBounds);
+ mTarget.adjustBoundsToAvoidConflict(displayBounds, existingTaskBounds, adjustedBounds);
+ assertEquals(startingBounds, adjustedBounds);
+ }
+
private TestActivityDisplay createNewActivityDisplay(int windowingMode) {
final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
display.setWindowingMode(windowingMode);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 33d5c04..26745a7 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1199,6 +1199,80 @@
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
/**
+ * Override the SPN Display Condition 2 integer bits (lsb). B2, B1 is the last two bits of the
+ * spn display condition coding.
+ *
+ * The default value -1 mean this field is not config.
+ *
+ * B1 = 0: display of registered PLMN name not required when registered PLMN is either HPLMN
+ * or a PLMN in the service provider PLMN list (see EF_SPDI).
+ * B1 = 1: display of registered PLMN name required when registered PLMN is either HPLMN or a
+ * PLMN in the service provider PLMN list(see EF_SPDI).
+ * B2 = 0: display of the service provider name is required when registered PLMN is neither
+ * HPLMN nor a PLMN in the service provider PLMN list(see EF_SPDI).
+ * B2 = 1: display of the service provider name is not required when registered PLMN is neither
+ * HPLMN nor a PLMN in the service provider PLMN list(see EF_SPDI).
+ *
+ * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.12 EF_SPN.
+ * @hide
+ */
+ public static final String KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT =
+ "spn_display_condition_override_int";
+
+ /**
+ * Override the SPDI - an array of PLMN(MCC + MNC) strings.
+ *
+ * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.66 EF_SPDI.
+ * @hide
+ */
+ public static final String KEY_SPDI_OVERRIDE_STRING_ARRAY = "spdi_override_string_array";
+
+ /**
+ * Override the EHPLMNs - an array of PLMN(MCC + MNC) strings.
+ *
+ * To allow provision for multiple HPLMN codes, PLMN codes that are present within this list
+ * shall replace the HPLMN code derived from the IMSI for PLMN selection purposes.
+ *
+ * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.84 EF_EHPLMN
+ * Reference: 3GPP TS 23.122 v15.6.0 Section 1.2 Equivalent HPLMN list
+ * @hide
+ */
+ public static final String KEY_EHPLMN_OVERRIDE_STRING_ARRAY = "ehplmn_override_string_array";
+
+ /**
+ * Override the PNN - a string array of comma-separated alpha long and short names:
+ * "alpha_long1, alpha_short1".
+ *
+ * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.58 EF_PNN.
+ * @hide
+ */
+ public static final String KEY_PNN_OVERRIDE_STRING_ARRAY = "pnn_override_string_array";
+
+ /**
+ * A string array of OPL records, each with comma-delimited data fields as follows:
+ * "plmn1,lactac_start,lactac_end,index".
+ *
+ * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.59 EF_OPL.
+ * @hide
+ */
+ public static final String KEY_OPL_OVERRIDE_STRING_ARRAY = "opl_override_opl_string_array";
+
+ /**
+ * Allow ERI rules to select a carrier name display string when using 3gpp2 access technologies.
+ *
+ * @hide
+ */
+ public static final String KEY_ALLOW_ERI_BOOL = "allow_cdma_eri_bool";
+
+ /**
+ * If true, use the carrier display name(SPN and PLMN) from the carrier display name resolver.
+ *
+ * @hide
+ */
+ public static final String KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL =
+ "enable_carrier_display_name_resolver_bool";
+
+ /**
* String to override sim country iso.
* Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where
* a SIM can have multiple MCC from different countries.
@@ -3023,6 +3097,13 @@
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+ sDefaults.putInt(KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, -1);
+ sDefaults.putStringArray(KEY_SPDI_OVERRIDE_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_PNN_OVERRIDE_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_OPL_OVERRIDE_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_EHPLMN_OVERRIDE_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_ALLOW_ERI_BOOL, false);
+ sDefaults.putBoolean(KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL, false);
sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
sDefaults.putString(KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING, null);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 2272dc9..8336d1b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -58,15 +58,15 @@
@UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mSignalStrength; // To be removed
private int mRssi;
- @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mRsrp;
- @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mRsrq;
- @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mRssnr;
- @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mCqi;
- @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.O)
+ @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mTimingAdvance;
private int mLevel;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 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/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index fa6cfcb..22f078f 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -635,7 +635,7 @@
!= second.getEmergencyServiceCategoryBitmask()) {
return false;
}
- if (first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
+ if (!first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
return false;
}
if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) {
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index c8ef82e..689abed 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -49,7 +49,6 @@
"libselinux",
"libui",
"libutils",
- "libvintf",
"libvndksupport",
"libtinyxml2",
"libunwindstack",
@@ -64,7 +63,7 @@
android_test {
name: "FrameworksNetTests",
defaults: ["FrameworksNetTests-jni-defaults"],
- srcs: ["java/**/*.java"],
+ srcs: ["java/**/*.java", "java/**/*.kt"],
platform_apis: true,
test_suites: ["device-tests"],
certificate: "platform",
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
new file mode 100644
index 0000000..f045369
--- /dev/null
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.net.ConnectivityManager.TYPE_ETHERNET
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.ConnectivityManager.TYPE_WIMAX
+import android.net.NetworkInfo.DetailedState.CONNECTED
+import android.net.NetworkInfo.DetailedState.DISCONNECTED
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.server.ConnectivityService.LegacyTypeTracker
+import com.android.server.connectivity.NetworkAgentInfo
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+
+const val UNSUPPORTED_TYPE = TYPE_WIMAX
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class LegacyTypeTrackerTest {
+ private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET)
+
+ private val mMockService = mock(ConnectivityService::class.java).apply {
+ doReturn(false).`when`(this).isDefaultNetwork(any())
+ }
+ private val mTracker = LegacyTypeTracker(mMockService).apply {
+ supportedTypes.forEach {
+ addSupportedType(it)
+ }
+ }
+
+ @Test
+ fun testSupportedTypes() {
+ try {
+ mTracker.addSupportedType(supportedTypes[0])
+ fail("Expected IllegalStateException")
+ } catch (expected: IllegalStateException) {}
+ supportedTypes.forEach {
+ assertTrue(mTracker.isTypeSupported(it))
+ }
+ assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE))
+ }
+
+ @Test
+ fun testAddNetwork() {
+ val mobileNai = mock(NetworkAgentInfo::class.java)
+ val wifiNai = mock(NetworkAgentInfo::class.java)
+ mTracker.add(TYPE_MOBILE, mobileNai)
+ mTracker.add(TYPE_WIFI, wifiNai)
+ assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ // Make sure adding a second NAI does not change the results.
+ val secondMobileNai = mock(NetworkAgentInfo::class.java)
+ mTracker.add(TYPE_MOBILE, secondMobileNai)
+ assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ // Make sure removing a network that wasn't added for this type is a no-op.
+ mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
+ assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+ assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ // Remove the top network for mobile and make sure the second one becomes the network
+ // of record for this type.
+ mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
+ assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
+ assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+ // Make sure adding a network for an unsupported type does not register it.
+ mTracker.add(UNSUPPORTED_TYPE, mobileNai)
+ assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE))
+ }
+
+ @Test
+ fun testBroadcastOnDisconnect() {
+ val mobileNai1 = mock(NetworkAgentInfo::class.java)
+ val mobileNai2 = mock(NetworkAgentInfo::class.java)
+ doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
+ mTracker.add(TYPE_MOBILE, mobileNai1)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
+ reset(mMockService)
+ doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
+ mTracker.add(TYPE_MOBILE, mobileNai2)
+ verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
+ mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE)
+ }
+}
diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp
new file mode 100644
index 0000000..ef1ad2c
--- /dev/null
+++ b/tests/net/smoketest/Android.bp
@@ -0,0 +1,17 @@
+// This test exists only because the jni_libs list for these tests is difficult to
+// maintain: the test itself only depends on libnetworkstatsfactorytestjni, but the test
+// fails to load that library unless *all* the dependencies of that library are explicitly
+// listed in jni_libs. This means that whenever any of the dependencies changes the test
+// starts failing and breaking presubmits in frameworks/base. We cannot easily put
+// FrameworksNetTests into global presubmit because they are at times flaky, but this
+// test is effectively empty beyond validating that the libraries load correctly, and
+// thus should be stable enough to put in global presubmit.
+//
+// TODO: remove this hack when there is a better solution for jni_libs that includes
+// dependent libraries.
+android_test {
+ name: "FrameworksNetSmokeTests",
+ defaults: ["FrameworksNetTests-jni-defaults"],
+ srcs: ["java/SmokeTest.java"],
+ test_suites: ["device-tests"],
+}
diff --git a/tests/net/smoketest/AndroidManifest.xml b/tests/net/smoketest/AndroidManifest.xml
new file mode 100644
index 0000000..f1b9feb
--- /dev/null
+++ b/tests/net/smoketest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.tests.net.smoketest">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.tests.net.smoketest"
+ android:label="Frameworks Networking Smoke Tests" />
+</manifest>
diff --git a/tests/net/smoketest/AndroidTest.xml b/tests/net/smoketest/AndroidTest.xml
new file mode 100644
index 0000000..ac366e4
--- /dev/null
+++ b/tests/net/smoketest/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks Networking Smoke Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksNetSmokeTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksNetSmokeTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.net.smoketest" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/net/smoketest/java/SmokeTest.java b/tests/net/smoketest/java/SmokeTest.java
new file mode 100644
index 0000000..7d6655f
--- /dev/null
+++ b/tests/net/smoketest/java/SmokeTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class SmokeTest {
+
+ @Test
+ public void testLoadJni() {
+ System.loadLibrary("networkstatsfactorytestjni");
+ assertEquals(0, 0x00);
+ }
+}