Merge "Handle tri-state location permission."
diff --git a/Android.bp b/Android.bp
index e9fb93e..dd5ef92 100644
--- a/Android.bp
+++ b/Android.bp
@@ -152,6 +152,9 @@
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
"core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
"core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
+ "core/java/android/hardware/face/IFaceService.aidl",
+ "core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl",
+ "core/java/android/hardware/face/IFaceServiceReceiver.aidl",
"core/java/android/hardware/fingerprint/IFingerprintService.aidl",
"core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
"core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
@@ -1251,6 +1254,9 @@
local_sourcepaths: frameworks_base_subdirs,
installable: false,
metalava_enabled: true,
+ metalava_annotations_enabled: true,
+ metalava_previous_api: ":public-api-for-metalava-annotations",
+ metalava_merge_annotations_dir: "tools/metalava/manual",
}
droiddoc {
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 9e172e7..3f19b8f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -245,6 +245,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/JAVA_LIBRARIES/platformprotos_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/statsdprotolite_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediadrm.signer.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index dfbabeb..a283e06 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -47,8 +47,7 @@
public void testCreateRenderNodeNoName() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- RenderNode node = RenderNode.create(null, null);
- node.destroy();
+ RenderNode.create(null, null);
}
}
@@ -56,8 +55,7 @@
public void testCreateRenderNode() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- RenderNode node = RenderNode.create("LinearLayout", null);
- node.destroy();
+ RenderNode.create("LinearLayout", null);
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 143b46d4..049a9d2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1202,6 +1202,9 @@
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public int backgroundRequestDetailResourceId;
+ field public int backgroundRequestResourceId;
+ field public int requestDetailResourceId;
field public int requestRes;
}
@@ -1209,6 +1212,7 @@
field public static final int FLAG_REMOVED = 2; // 0x2
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public java.lang.String backgroundPermission;
field public int requestRes;
}
@@ -3923,6 +3927,7 @@
method public void readFromParcel(android.os.Parcel);
method public int sectionCount();
method public void setAll(boolean);
+ method public void setPrivacyPolicy(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 6e3c768..1e76d4a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -276,6 +276,7 @@
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+ field public java.lang.String backgroundPermission;
}
public final class ShortcutInfo implements android.os.Parcelable {
@@ -654,6 +655,7 @@
method public void readFromParcel(android.os.Parcel);
method public int sectionCount();
method public void setAll(boolean);
+ method public void setPrivacyPolicy(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 453fb72..6943dab 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -27,7 +27,9 @@
LOCAL_SHARED_LIBRARIES += \
libandroidthings \
+ libandroidthings_protos \
libchrome \
+ libprotobuf-cpp-lite \
LOCAL_STATIC_LIBRARIES += \
libjsoncpp
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
index 8b475d3..3d288e4 100644
--- a/cmds/bootanimation/iot/Android.mk
+++ b/cmds/bootanimation/iot/Android.mk
@@ -25,9 +25,11 @@
LOCAL_SHARED_LIBRARIES := \
libandroidthings \
+ libandroidthings_protos \
libbase \
libchrome \
liblog \
+ libprotobuf-cpp-lite \
LOCAL_STATIC_LIBRARIES += \
libjsoncpp
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
index 2cf1c19..30a9b28 100644
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -22,10 +22,13 @@
#include <fcntl.h>
#include <android-base/file.h>
+#include <json/json.h>
#include <utils/Log.h>
using android::base::ReadFileToString;
using android::base::RemoveFileIfExists;
+using android::base::WriteStringToFile;
+using Json::ArrayIndex;
using Json::Reader;
using Json::Value;
@@ -33,23 +36,34 @@
namespace {
-// Keys for supporting a silent boot and user-defined BootAction parameters.
-constexpr const char *kKeySilentBoot = "silent_boot";
-constexpr const char* kKeyParams = "params";
+// Keys for deprecated parameters. Devices that OTA from N to O and that used
+// the hidden BootParameters API will store these in the JSON blob. To support
+// the transition from N to O, these keys are mapped to the new parameters.
+constexpr const char *kKeyLegacyVolume = "volume";
+constexpr const char *kKeyLegacyAnimationsDisabled = "boot_animation_disabled";
+constexpr const char *kKeyLegacyParamNames = "param_names";
+constexpr const char *kKeyLegacyParamValues = "param_values";
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+constexpr const char *kNextBootFile = "/data/misc/bootanimation/next_boot.proto";
+constexpr const char *kLastBootFile = "/data/misc/bootanimation/last_boot.proto";
-void swapBootConfigs() {
- // rename() will fail if next_boot.json doesn't exist, so delete
- // last_boot.json manually first.
+constexpr const char *kLegacyNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char *kLegacyLastBootFile = "/data/misc/bootanimation/last_boot.json";
+
+void removeLegacyFiles() {
std::string err;
- if (!RemoveFileIfExists(kLastBootFile, &err))
- ALOGE("Unable to delete last boot file: %s", err.c_str());
+ if (!RemoveFileIfExists(kLegacyLastBootFile, &err)) {
+ ALOGW("Unable to delete %s: %s", kLegacyLastBootFile, err.c_str());
+ }
- if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
- ALOGE("Unable to swap boot files: %s", strerror(errno));
+ err.clear();
+ if (!RemoveFileIfExists(kLegacyNextBootFile, &err)) {
+ ALOGW("Unable to delete %s: %s", kLegacyNextBootFile, err.c_str());
+ }
+}
+void createNextBootFile() {
+ errno = 0;
int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
if (fd == -1) {
ALOGE("Unable to create next boot file: %s", strerror(errno));
@@ -64,54 +78,120 @@
} // namespace
+// Renames the 'next' boot file to the 'last' file and reads its contents.
+bool BootParameters::swapAndLoadBootConfigContents(const char *lastBootFile,
+ const char *nextBootFile,
+ std::string *contents) {
+ if (!ReadFileToString(nextBootFile, contents)) {
+ RemoveFileIfExists(lastBootFile);
+ return false;
+ }
+
+ errno = 0;
+ if (rename(nextBootFile, lastBootFile) && errno != ENOENT)
+ ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+ return true;
+}
+
BootParameters::BootParameters() {
- swapBootConfigs();
loadParameters();
}
-void BootParameters::loadParameters() {
- std::string contents;
- if (!ReadFileToString(kLastBootFile, &contents)) {
- if (errno != ENOENT)
- ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+// Saves the boot parameters state to disk so the framework can read it.
+void BootParameters::storeParameters() {
+ errno = 0;
+ if (!WriteStringToFile(mProto.SerializeAsString(), kLastBootFile)) {
+ ALOGE("Failed to write boot parameters to %s: %s", kLastBootFile, strerror(errno));
+ }
+ // WriteStringToFile sets the file permissions to 0666, but these are not
+ // honored by the system.
+ errno = 0;
+ if (chmod(kLastBootFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
+ ALOGE("Failed to set permissions for %s: %s", kLastBootFile, strerror(errno));
+ }
+}
+
+// Load the boot parameters from disk, try the old location and format if the
+// file does not exist. Note:
+// - Parse errors result in defaults being used (a normal boot).
+// - Legacy boot parameters default to a silent boot.
+void BootParameters::loadParameters() {
+ // Precedence is given to the new file format (.proto).
+ std::string contents;
+ if (swapAndLoadBootConfigContents(kLastBootFile, kNextBootFile, &contents)) {
+ parseBootParameters(contents);
+ } else if (swapAndLoadBootConfigContents(kLegacyLastBootFile, kLegacyNextBootFile, &contents)) {
+ parseLegacyBootParameters(contents);
+ storeParameters();
+ removeLegacyFiles();
+ }
+
+ createNextBootFile();
+}
+
+void BootParameters::parseBootParameters(const std::string &contents) {
+ if (!mProto.ParseFromString(contents)) {
+ ALOGW("Failed to parse parameters from %s", kLastBootFile);
return;
}
- loadParameters(contents);
+ loadStateFromProto();
}
-// If the boot parameters -
-// - File is missing, we assume a normal, non-silent boot.
-// - Are well-formed, initially assume a normal, non-silent boot and parse.
-void BootParameters::loadParameters(const std::string& raw_json) {
- if (!Reader().parse(raw_json, mJson)) {
- return;
- }
-
- parseBootParameters();
-}
-
-void BootParameters::parseBootParameters() {
- // A missing key returns a safe, missing value.
- // Ignore invalid or missing JSON parameters.
- Value &jsonValue = mJson[kKeySilentBoot];
- if (jsonValue.isBool()) {
- mIsSilentBoot = jsonValue.asBool();
+// Parses the JSON in the proto.
+void BootParameters::parseLegacyBootParameters(const std::string &contents) {
+ Value json;
+ if (!Reader().parse(contents, json)) {
+ ALOGW("Failed to parse parameters from %s", kLegacyLastBootFile);
+ return;
}
- jsonValue = mJson[kKeyParams];
- if (jsonValue.isObject()) {
- // getMemberNames returns a copy of the keys which must be stored.
- mKeys = jsonValue.getMemberNames();
- for (auto &key : mKeys) {
- Value &value = jsonValue[key];
- if (value.isString()) {
- mParameters.push_back(
- {.key = key.c_str(), .value = value.asCString()});
+ int volume = 0;
+ bool bootAnimationDisabled = true;
+
+ Value &jsonValue = json[kKeyLegacyVolume];
+ if (jsonValue.isIntegral()) {
+ volume = jsonValue.asInt();
+ }
+
+ jsonValue = json[kKeyLegacyAnimationsDisabled];
+ if (jsonValue.isIntegral()) {
+ bootAnimationDisabled = jsonValue.asInt() == 1;
+ }
+
+ // Assume a silent boot unless all of the following are true -
+ // 1. The volume is neither 0 nor -1000 (the legacy default value).
+ // 2. The boot animations are explicitly enabled.
+ // Note: brightness was never used.
+ mProto.set_silent_boot((volume == 0) || (volume == -1000) || bootAnimationDisabled);
+
+ Value &keys = json[kKeyLegacyParamNames];
+ Value &values = json[kKeyLegacyParamValues];
+ if (keys.isArray() && values.isArray() && (keys.size() == values.size())) {
+ for (ArrayIndex i = 0; i < keys.size(); ++i) {
+ auto &key = keys[i];
+ auto &value = values[i];
+ if (key.isString() && value.isString()) {
+ auto userParameter = mProto.add_user_parameter();
+ userParameter->set_key(key.asString());
+ userParameter->set_value(value.asString());
}
}
}
+
+ loadStateFromProto();
+}
+
+void BootParameters::loadStateFromProto() {
+ // A missing key returns a safe, default value.
+ // Ignore invalid or missing parameters.
+ mIsSilentBoot = mProto.silent_boot();
+
+ for (const auto ¶m : mProto.user_parameter()) {
+ mParameters.push_back({.key = param.key().c_str(), .value = param.value().c_str()});
+ }
}
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
index 49c34dd..cbd1ca6 100644
--- a/cmds/bootanimation/iot/BootParameters.h
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -22,7 +22,7 @@
#include <vector>
#include <boot_action/boot_action.h> // libandroidthings native API.
-#include <json/json.h>
+#include <boot_parameters.pb.h>
namespace android {
@@ -39,22 +39,33 @@
// Returns the additional boot parameters that were set on reboot.
const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
- // Exposed for testing. Updates the parameters with new JSON values.
- void loadParameters(const std::string& raw_json);
-private:
+ // Exposed for testing. Sets the parameters to the serialized proto.
+ void parseBootParameters(const std::string &contents);
+
+ // For devices that OTA from N to O.
+ // Exposed for testing. Sets the parameters to the raw JSON.
+ void parseLegacyBootParameters(const std::string &contents);
+
+ // Exposed for testing. Loads the contents from |nextBootFile| and replaces
+ // |lastBootFile| with |nextBootFile|.
+ static bool swapAndLoadBootConfigContents(const char *lastBootFile, const char *nextBootFile,
+ std::string *contents);
+
+ private:
void loadParameters();
- void parseBootParameters();
+ // Replaces the legacy JSON blob with the updated version, allowing the
+ // framework to read it.
+ void storeParameters();
+
+ void loadStateFromProto();
bool mIsSilentBoot = false;
std::vector<ABootActionParameter> mParameters;
- // Store parsed JSON because mParameters makes a shallow copy.
- Json::Value mJson;
-
- // Store parameter keys because mParameters makes a shallow copy.
- Json::Value::Members mKeys;
+ // Store the proto because mParameters makes a shallow copy.
+ android::things::proto::BootParameters mProto;
};
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters_test.cpp b/cmds/bootanimation/iot/BootParameters_test.cpp
index 27d7d6f..d55bce6 100644
--- a/cmds/bootanimation/iot/BootParameters_test.cpp
+++ b/cmds/bootanimation/iot/BootParameters_test.cpp
@@ -16,6 +16,13 @@
#include "BootParameters.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <boot_parameters.pb.h>
#include <gtest/gtest.h>
namespace android {
@@ -23,50 +30,51 @@
namespace {
TEST(BootParametersTest, TestNoBootParametersIsNotSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters("");
+ android::things::proto::BootParameters proto;
- ASSERT_FALSE(boot_parameters.isSilentBoot());
- ASSERT_EQ(0u, boot_parameters.getParameters().size());
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
}
TEST(BootParametersTest, TestParseIsSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
- {
- "silent_boot":true,
- "params":{}
- }
- )");
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(true);
- ASSERT_TRUE(boot_parameters.isSilentBoot());
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
}
TEST(BootParametersTest, TestParseIsNotSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
- {
- "silent_boot":false,
- "params":{}
- }
- )");
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(false);
- ASSERT_FALSE(boot_parameters.isSilentBoot());
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
}
TEST(BootParametersTest, TestParseBootParameters) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
- {
- "silent_boot":false,
- "params":{
- "key1":"value1",
- "key2":"value2"
- }
- }
- )");
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(false);
- auto ¶meters = boot_parameters.getParameters();
+ auto userParameter = proto.add_user_parameter();
+ userParameter->set_key("key1");
+ userParameter->set_value("value1");
+
+ userParameter = proto.add_user_parameter();
+ userParameter->set_key("key2");
+ userParameter->set_value("value2");
+
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ auto ¶meters = bootParameters.getParameters();
ASSERT_EQ(2u, parameters.size());
ASSERT_STREQ(parameters[0].key, "key1");
ASSERT_STREQ(parameters[0].value, "value1");
@@ -74,35 +82,182 @@
ASSERT_STREQ(parameters[1].value, "value2");
}
-TEST(BootParametersTest, TestParseMissingParametersIsNotSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
+TEST(BootParametersTest, TestParseLegacyDisableBootAnimationIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
{
- "params":{}
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":[],
+ "param_values":[]
}
)");
- ASSERT_FALSE(boot_parameters.isSilentBoot());
+ ASSERT_TRUE(bootParameters.isSilentBoot());
}
-TEST(BootParametersTest, TestParseMalformedParametersAreSkipped) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
+TEST(BootParametersTest, TestParseLegacyZeroVolumeIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
{
- "silent_boot":false,
- "params":{
- "key1":123,
- "key2":"value2"
- }
+ "brightness":200,
+ "volume":0,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
}
)");
- auto ¶meters = boot_parameters.getParameters();
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyDefaultVolumeIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":-1000,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyNotSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyParameters) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":["key1", "key2"],
+ "param_values":["value1", "value2"]
+ }
+ )");
+
+ auto parameters = bootParameters.getParameters();
+ ASSERT_EQ(2u, parameters.size());
+ ASSERT_STREQ(parameters[0].key, "key1");
+ ASSERT_STREQ(parameters[0].value, "value1");
+ ASSERT_STREQ(parameters[1].key, "key2");
+ ASSERT_STREQ(parameters[1].value, "value2");
+}
+
+TEST(BootParametersTest, TestParseLegacyZeroParameters) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMalformedLegacyParametersAreSkipped) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":["key1", "key2"],
+ "param_values":[1, "value2"]
+ }
+ )");
+
+ auto parameters = bootParameters.getParameters();
ASSERT_EQ(1u, parameters.size());
ASSERT_STREQ(parameters[0].key, "key2");
ASSERT_STREQ(parameters[0].value, "value2");
}
+TEST(BootParametersTest, TestLegacyUnequalParameterSizesAreSkipped) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":["key1", "key2"],
+ "param_values":["value1"]
+ }
+ )");
+
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMissingLegacyBootParametersIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500
+ }
+ )");
+
+ EXPECT_TRUE(bootParameters.isSilentBoot());
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestLastFileIsRemovedOnError) {
+ TemporaryFile lastFile;
+ TemporaryDir tempDir;
+ std::string nonExistentFilePath(std::string(tempDir.path) + "/nonexistent");
+ std::string contents;
+
+ BootParameters::swapAndLoadBootConfigContents(lastFile.path, nonExistentFilePath.c_str(),
+ &contents);
+
+ struct stat buf;
+ ASSERT_EQ(-1, lstat(lastFile.path, &buf));
+ ASSERT_TRUE(contents.empty());
+}
+
+TEST(BootParametersTest, TestNextFileIsRemovedLastFileExistsOnSuccess) {
+ TemporaryFile lastFile;
+ TemporaryFile nextFile;
+
+ base::WriteStringToFile("foo", nextFile.path);
+
+ std::string contents;
+ // Expected side effects:
+ // - |next_file| is moved to |last_file|
+ // - |contents| is the contents of |next_file| before being moved.
+ BootParameters::swapAndLoadBootConfigContents(lastFile.path, nextFile.path, &contents);
+
+ struct stat buf;
+ ASSERT_EQ(0, lstat(lastFile.path, &buf));
+ ASSERT_EQ(-1, lstat(nextFile.path, &buf));
+ ASSERT_EQ(contents, "foo");
+
+ contents.clear();
+ ASSERT_TRUE(base::ReadFileToString(lastFile.path, &contents));
+ ASSERT_EQ(contents, "foo");
+}
+
} // namespace
} // namespace android
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 7c1a555..376b13c 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -46,6 +46,7 @@
private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+ private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
private IDevicePolicyManager mDevicePolicyManager;
@@ -84,6 +85,9 @@
"feature development to prevent triggering restriction on setting freeze " +
"periods.\n" +
"\n" +
+ "dpm " + COMMAND_FORCE_NETWORK_LOGS + ": makes all network logs available to " +
+ "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
+ "\n" +
"dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
"the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
}
@@ -114,6 +118,9 @@
case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
runClearFreezePeriodRecord();
break;
+ case COMMAND_FORCE_NETWORK_LOGS:
+ runForceNetworkLogs();
+ break;
case COMMAND_FORCE_SECURITY_LOGS:
runForceSecurityLogs();
break;
@@ -122,6 +129,18 @@
}
}
+ private void runForceNetworkLogs() throws RemoteException, InterruptedException {
+ while (true) {
+ final long toWait = mDevicePolicyManager.forceNetworkLogs();
+ if (toWait == 0) {
+ break;
+ }
+ System.out.println("We have to wait for " + toWait + " milliseconds...");
+ Thread.sleep(toWait);
+ }
+ System.out.println("Success");
+ }
+
private void runForceSecurityLogs() throws RemoteException, InterruptedException {
while (true) {
final long toWait = mDevicePolicyManager.forceSecurityLogs();
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5e3b85ba..0b5e358 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -41,7 +41,7 @@
src/external/SubsystemSleepStatePuller.cpp \
src/external/ResourceHealthManagerPuller.cpp \
src/external/ResourceThermalManagerPuller.cpp \
- src/external/StatsPullerManagerImpl.cpp \
+ src/external/StatsPullerManager.cpp \
src/external/puller_util.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 50ed18d..067b6ed 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -362,11 +362,12 @@
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
- sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec * NS_PER_SEC,
- [](const ConfigKey&){return true;});
+ sp<StatsLogProcessor> processor =
+ new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; });
processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
return processor;
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 1378ce5..ab0aa25 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -72,18 +72,20 @@
#define STATS_DATA_DIR "/data/misc/stats-data"
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor,
const int64_t timeBaseNs,
const std::function<bool(const ConfigKey&)>& sendBroadcast)
: mUidMap(uidMap),
+ mPullerManager(pullerManager),
mAnomalyAlarmMonitor(anomalyAlarmMonitor),
mPeriodicAlarmMonitor(periodicAlarmMonitor),
mSendBroadcast(sendBroadcast),
mTimeBaseNs(timeBaseNs),
mLargestTimestampSeen(0),
mLastTimestampSeen(0) {
- mStatsPullerManager.ForceClearPullerCache();
+ mPullerManager->ForceClearPullerCache();
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -238,7 +240,7 @@
int64_t curTimeSec = getElapsedRealtimeSec();
if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
- mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
+ mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
mLastPullerCacheClearTimeSec = curTimeSec;
}
@@ -266,8 +268,8 @@
const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
VLOG("Updated configuration for key %s", key.ToString().c_str());
sp<MetricsManager> newMetricsManager =
- new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap,
- mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
+ new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
+ mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
if (newMetricsManager->isConfigValid()) {
mUidMap->OnConfigUpdated(key);
if (newMetricsManager->shouldAddUidMapListener()) {
@@ -453,7 +455,7 @@
mLastBroadcastTimes.erase(key);
if (mMetricsManagers.empty()) {
- mStatsPullerManager.ForceClearPullerCache();
+ mPullerManager->ForceClearPullerCache();
}
}
@@ -538,7 +540,7 @@
void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- mStatsPullerManager.OnAlarmFired(timestampNs);
+ mPullerManager->OnAlarmFired(timestampNs);
}
int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index b175b3c..05cf0c1 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -45,7 +45,8 @@
class StatsLogProcessor : public ConfigListener {
public:
- StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
const int64_t timeBaseNs,
const std::function<bool(const ConfigKey&)>& sendBroadcast);
@@ -126,7 +127,7 @@
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
- StatsPullerManager mStatsPullerManager;
+ sp<StatsPullerManager> mPullerManager; // Reference to StatsPullerManager
sp<AlarmMonitor> mAnomalyAlarmMonitor;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 10c04f6..ae44ee9 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -150,25 +150,26 @@
})) {
mUidMap = new UidMap();
+ mPullerManager = new StatsPullerManager();
StatsPuller::SetUidMap(mUidMap);
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
- getElapsedRealtimeNs(), [this](const ConfigKey& key) {
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- auto receiver = mConfigManager->GetConfigReceiver(key);
- if (sc == nullptr) {
- VLOG("Could not find StatsCompanionService");
- return false;
- } else if (receiver == nullptr) {
- VLOG("Statscompanion could not find a broadcast receiver for %s",
- key.ToString().c_str());
- return false;
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
- return true;
- }
- }
- );
+ mProcessor = new StatsLogProcessor(
+ mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
+ getElapsedRealtimeNs(), [this](const ConfigKey& key) {
+ sp<IStatsCompanionService> sc = getStatsCompanionService();
+ auto receiver = mConfigManager->GetConfigReceiver(key);
+ if (sc == nullptr) {
+ VLOG("Could not find StatsCompanionService");
+ return false;
+ } else if (receiver == nullptr) {
+ VLOG("Statscompanion could not find a broadcast receiver for %s",
+ key.ToString().c_str());
+ return false;
+ } else {
+ sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ return true;
+ }
+ });
mConfigManager->AddListener(mProcessor);
@@ -711,7 +712,7 @@
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
vector<shared_ptr<LogEvent> > stats;
- if (mStatsPullerManager.Pull(s, getElapsedRealtimeNs(), &stats)) {
+ if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
@@ -739,7 +740,7 @@
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
ipc->getCallingPid(), ipc->getCallingUid());
if (checkCallingPermission(String16(kPermissionDump))) {
- int cleared = mStatsPullerManager.ForceClearPullerCache();
+ int cleared = mPullerManager->ForceClearPullerCache();
fprintf(out, "Puller removed %d cached data!\n", cleared);
return NO_ERROR;
} else {
@@ -870,7 +871,7 @@
}
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
IInterface::asBinder(statsCompanion)->linkToDeath(this);
- mStatsPullerManager.SetStatsCompanionService(statsCompanion);
+ mPullerManager->SetStatsCompanionService(statsCompanion);
mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
@@ -1014,7 +1015,7 @@
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
- mStatsPullerManager.SetStatsCompanionService(nullptr);
+ mPullerManager->SetStatsCompanionService(nullptr);
}
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index b3a4776..0a11f7c 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -246,9 +246,9 @@
sp<UidMap> mUidMap;
/**
- * Fetches external metrics.
+ * Fetches external metrics
*/
- StatsPullerManager mStatsPullerManager;
+ sp<StatsPullerManager> mPullerManager;
/**
* Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index b29e979..436a880 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -18,10 +18,10 @@
#include "Log.h"
#include "StatsPuller.h"
+#include "StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "puller_util.h"
#include "stats_log_util.h"
-#include "StatsPullerManagerImpl.h"
namespace android {
namespace os {
@@ -35,7 +35,7 @@
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
- mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
+ mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
similarity index 92%
rename from cmds/statsd/src/external/StatsPullerManagerImpl.cpp
rename to cmds/statsd/src/external/StatsPullerManager.cpp
index c020f9c..06edff9 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -29,7 +29,7 @@
#include "ResourceHealthManagerPuller.h"
#include "ResourceThermalManagerPuller.h"
#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManagerImpl.h"
+#include "StatsPullerManager.h"
#include "SubsystemSleepStatePuller.h"
#include "statslog.h"
@@ -49,7 +49,7 @@
// Values smaller than this may require to update the alarm.
const int64_t NO_ALARM_UPDATE = INT64_MAX;
-const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
+const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// wifi_bytes_transfer
{android::util::WIFI_BYTES_TRANSFER,
{{2, 3, 4, 5},
@@ -173,10 +173,10 @@
// temperature
{android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
-StatsPullerManagerImpl::StatsPullerManagerImpl() : mNextPullTimeNs(NO_ALARM_UPDATE) {
+StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
}
-bool StatsPullerManagerImpl::Pull(const int tagId, const int64_t timeNs,
+bool StatsPullerManager::Pull(const int tagId, const int64_t timeNs,
vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
@@ -190,16 +190,11 @@
}
}
-StatsPullerManagerImpl& StatsPullerManagerImpl::GetInstance() {
- static StatsPullerManagerImpl instance;
- return instance;
-}
-
-bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const {
+bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
}
-void StatsPullerManagerImpl::updateAlarmLocked() {
+void StatsPullerManager::updateAlarmLocked() {
if (mNextPullTimeNs == NO_ALARM_UPDATE) {
VLOG("No need to set alarms. Skipping");
return;
@@ -214,7 +209,7 @@
return;
}
-void StatsPullerManagerImpl::SetStatsCompanionService(
+void StatsPullerManager::SetStatsCompanionService(
sp<IStatsCompanionService> statsCompanionService) {
AutoMutex _l(mLock);
sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
@@ -227,7 +222,7 @@
}
}
-void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
+void StatsPullerManager::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
int64_t nextPullTimeNs, int64_t intervalNs) {
AutoMutex _l(mLock);
auto& receivers = mReceivers[tagId];
@@ -262,7 +257,7 @@
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
-void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
+void StatsPullerManager::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
AutoMutex _l(mLock);
if (mReceivers.find(tagId) == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
@@ -278,7 +273,7 @@
}
}
-void StatsPullerManagerImpl::OnAlarmFired(const int64_t currentTimeNs) {
+void StatsPullerManager::OnAlarmFired(const int64_t currentTimeNs) {
AutoMutex _l(mLock);
int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
@@ -331,7 +326,7 @@
updateAlarmLocked();
}
-int StatsPullerManagerImpl::ForceClearPullerCache() {
+int StatsPullerManager::ForceClearPullerCache() {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second.puller->ForceClearCache();
@@ -339,7 +334,7 @@
return totalCleared;
}
-int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 50ffe17..45efc4a 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,54 +16,87 @@
#pragma once
-#include "StatsPullerManagerImpl.h"
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <list>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
-class StatsPullerManager {
- public:
- virtual ~StatsPullerManager() {}
+typedef struct {
+ // The field numbers of the fields that need to be summed when merging
+ // isolated uid with host uid.
+ std::vector<int> additiveFields;
+ // The field numbers of the fields that can't be merged when merging
+ // data belong to isolated uid and host uid.
+ std::vector<int> nonAdditiveFields;
+ // How long should the puller wait before doing an actual pull again. Default
+ // 1 sec. Set this to 0 if this is handled elsewhere.
+ int64_t coolDownNs = 1 * NS_PER_SEC;
+ // The actual puller
+ sp<StatsPuller> puller;
+} PullAtomInfo;
+
+class StatsPullerManager : public virtual RefBase {
+public:
+ StatsPullerManager();
+
+ virtual ~StatsPullerManager() {
+ }
virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
- int64_t intervalNs) {
- mPullerManager.RegisterReceiver(tagId, receiver, nextPullTimeNs, intervalNs);
- };
+ int64_t intervalNs);
- virtual void UnRegisterReceiver(int tagId, wp <PullDataReceiver> receiver) {
- mPullerManager.UnRegisterReceiver(tagId, receiver);
- };
+ virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
// Verify if we know how to pull for this matcher
- bool PullerForMatcherExists(int tagId) {
- return mPullerManager.PullerForMatcherExists(tagId);
- }
+ bool PullerForMatcherExists(int tagId) const;
- void OnAlarmFired(const int64_t currentTimeNs) {
- mPullerManager.OnAlarmFired(currentTimeNs);
- }
+ void OnAlarmFired(const int64_t timeNs);
- virtual bool Pull(const int tagId, const int64_t timesNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- return mPullerManager.Pull(tagId, timesNs, data);
- }
+ virtual bool Pull(const int tagId, const int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data);
- int ForceClearPullerCache() {
- return mPullerManager.ForceClearPullerCache();
- }
+ int ForceClearPullerCache();
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
- mPullerManager.SetStatsCompanionService(statsCompanionService);
- }
+ int ClearPullerCacheIfNecessary(int64_t timestampNs);
- int ClearPullerCacheIfNecessary(int64_t timestampNs) {
- return mPullerManager.ClearPullerCacheIfNecessary(timestampNs);
- }
+ void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
- private:
- StatsPullerManagerImpl
- & mPullerManager = StatsPullerManagerImpl::GetInstance();
+ const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+
+private:
+ sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ typedef struct {
+ int64_t nextPullTimeNs;
+ int64_t intervalNs;
+ wp<PullDataReceiver> receiver;
+ } ReceiverInfo;
+
+ // mapping from simple matcher tagId to receivers
+ std::map<int, std::list<ReceiverInfo>> mReceivers;
+
+ // locks for data receiver and StatsCompanionService changes
+ Mutex mLock;
+
+ void updateAlarmLocked();
+
+ int64_t mNextPullTimeNs;
+
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+ FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
deleted file mode 100644
index 56d04b4..0000000
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include <list>
-#include "PullDataReceiver.h"
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-typedef struct {
- // The field numbers of the fields that need to be summed when merging
- // isolated uid with host uid.
- std::vector<int> additiveFields;
- // The field numbers of the fields that can't be merged when merging
- // data belong to isolated uid and host uid.
- std::vector<int> nonAdditiveFields;
- // How long should the puller wait before doing an actual pull again. Default
- // 1 sec. Set this to 0 if this is handled elsewhere.
- int64_t coolDownNs = 1 * NS_PER_SEC;
- // The actual puller
- sp<StatsPuller> puller;
-} PullAtomInfo;
-
-class StatsPullerManagerImpl : public virtual RefBase {
-public:
- static StatsPullerManagerImpl& GetInstance();
-
- void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
- int64_t intervalNs);
-
- void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
-
- // Verify if we know how to pull for this matcher
- bool PullerForMatcherExists(int tagId) const;
-
- void OnAlarmFired(const int64_t timeNs);
-
- bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
-
- int ForceClearPullerCache();
-
- int ClearPullerCacheIfNecessary(int64_t timestampNs);
-
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
-
- const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
-
- private:
- StatsPullerManagerImpl();
-
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- typedef struct {
- int64_t nextPullTimeNs;
- int64_t intervalNs;
- wp<PullDataReceiver> receiver;
- } ReceiverInfo;
-
- // mapping from simple matcher tagId to receivers
- std::map<int, std::list<ReceiverInfo>> mReceivers;
-
- // locks for data receiver and StatsCompanionService changes
- Mutex mLock;
-
- void updateAlarmLocked();
-
- int64_t mNextPullTimeNs;
-
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 57fe10e..ea7fa97 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -17,7 +17,7 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "StatsPullerManagerImpl.h"
+#include "StatsPullerManager.h"
#include "puller_util.h"
#include "statslog.h"
@@ -107,8 +107,8 @@
*/
void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
int tagId) {
- if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
- StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+ if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
+ StatsPullerManager::kAllPullAtomInfo.end()) {
VLOG("Unknown pull atom id %d", tagId);
return;
}
@@ -121,9 +121,9 @@
uidField = it->second; // uidField is the field number in proto,
}
const vector<int>& additiveFields =
- StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+ StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
const vector<int>& nonAdditiveFields =
- StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
+ StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
// map of host uid to their position in the original vector
map<int, vector<int>> hostPosition;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index aabd361..d75bb10 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -72,9 +72,9 @@
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
const int64_t timeBaseNs, const int64_t startTimeNs,
- shared_ptr<StatsPullerManager> statsPullerManager)
+ const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
- mStatsPullerManager(statsPullerManager),
+ mPullerManager(pullerManager),
mPullTagId(pullTagId),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
@@ -127,8 +127,8 @@
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mStatsPullerManager->RegisterReceiver(
- mPullTagId, this, getCurrentBucketEndTimeNs(), mBucketSizeNs);
+ mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mBucketSizeNs);
}
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -136,19 +136,10 @@
mConditionSliced);
}
-// for testing
-GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimeNs)
- : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
- make_shared<StatsPullerManager>()) {
-}
-
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
@@ -336,7 +327,7 @@
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mStatsPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
return;
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index c74f792..62ec27eb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -58,7 +58,8 @@
public:
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager);
virtual ~GaugeMetricProducer();
@@ -94,13 +95,6 @@
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
- // for testing
- GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- std::shared_ptr<StatsPullerManager> statsPullerManager);
-
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
@@ -123,7 +117,7 @@
int mTagId;
- std::shared_ptr<StatsPullerManager> mStatsPullerManager;
+ sp<StatsPullerManager> mPullerManager;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 4fac0e1..213f9ab 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -56,10 +56,12 @@
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
const int64_t timeBaseNs, const int64_t currentTimeNs,
- const sp<UidMap> &uidMap,
+ const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor)
- : mConfigKey(key), mUidMap(uidMap),
+ : mConfigKey(key),
+ mUidMap(uidMap),
mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
mTtlEndNs(-1),
mLastReportTimeNs(currentTimeNs),
@@ -67,12 +69,11 @@
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
- mConfigValid =
- initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers,
- mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
- mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
- mTrackerToConditionMap, mNoReportMetricIds);
+ mConfigValid = initStatsdConfig(
+ key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
+ mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
+ mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6f4db48..dfbb69f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,6 +16,7 @@
#pragma once
+#include "external/StatsPullerManager.h"
#include "anomaly/AlarmMonitor.h"
#include "anomaly/AlarmTracker.h"
#include "anomaly/AnomalyTracker.h"
@@ -36,9 +37,10 @@
// A MetricsManager is responsible for managing metrics from one single config source.
class MetricsManager : public PackageInfoListener {
public:
- MetricsManager(const ConfigKey& configKey, const StatsdConfig& config,
- const int64_t timeBaseNs, const int64_t currentTimeNs,
- const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<UidMap>& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor);
virtual ~MetricsManager();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 41e55cb..ef8b6cc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -74,10 +74,10 @@
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
const int64_t timeBaseNs, const int64_t startTimestampNs,
- shared_ptr<StatsPullerManager> statsPullerManager)
+ const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+ mPullerManager(pullerManager),
mValueField(metric.value_field()),
- mStatsPullerManager(statsPullerManager),
mPullTagId(pullTagId),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
@@ -127,27 +127,18 @@
// Kicks off the puller immediately.
flushIfNeededLocked(startTimestampNs);
if (mPullTagId != -1) {
- mStatsPullerManager->RegisterReceiver(
- mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
+ mPullerManager->RegisterReceiver(mPullTagId, this,
+ mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
VLOG("value metric %lld created. bucket size %lld start_time: %lld",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
-// for testing
-ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimeNs)
- : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
- make_shared<StatsPullerManager>()) {
-}
-
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
if (mPullTagId != -1) {
- mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
@@ -283,7 +274,7 @@
if (mPullTagId != -1) {
vector<shared_ptr<LogEvent>> allData;
- if (mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
+ if (mPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
if (allData.size() == 0) {
return;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index cb6b051..75f3113 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -40,7 +40,8 @@
public:
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
+ const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager);
virtual ~ValueMetricProducer();
@@ -53,7 +54,7 @@
if (mPullTagId != -1 && (mCondition == true || mConditionTrackerIndex < 0) ) {
vector<shared_ptr<LogEvent>> allData;
- mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
+ mPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
if (allData.size() == 0) {
// This shouldn't happen since this valuemetric is not useful now.
}
@@ -112,16 +113,10 @@
void dropDataLocked(const int64_t dropTimeNs) override;
+ sp<StatsPullerManager> mPullerManager;
+
const FieldMatcher mValueField;
- std::shared_ptr<StatsPullerManager> mStatsPullerManager;
-
- // for testing
- ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
- std::shared_ptr<StatsPullerManager> statsPullerManager);
-
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 811a00e..deb9893 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -262,9 +262,10 @@
return true;
}
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
- const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
- UidMap& uidMap, const unordered_map<int64_t, int>& logTrackerMap,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+ const int64_t currentTimeNs, UidMap& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int64_t, int>& logTrackerMap,
const unordered_map<int64_t, int>& conditionTrackerMap,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -465,9 +466,9 @@
}
}
- sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
- wizard, pullTagId,
- timeBaseTimeNs, currentTimeNs);
+ sp<MetricProducer> valueProducer =
+ new ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId,
+ timeBaseTimeNs, currentTimeNs, pullerManager);
allMetricProducers.push_back(valueProducer);
}
@@ -525,8 +526,9 @@
}
}
- sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, wizard, pullTagId, timeBaseTimeNs, currentTimeNs);
+ sp<MetricProducer> gaugeProducer =
+ new GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId,
+ timeBaseTimeNs, currentTimeNs, pullerManager);
allMetricProducers.push_back(gaugeProducer);
}
for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -645,10 +647,10 @@
}
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor,
- const int64_t timeBaseNs, const int64_t currentTimeNs,
- set<int>& allTagIds,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allAtomMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
@@ -674,9 +676,8 @@
return false;
}
- if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap,
- logTrackerMap, conditionTrackerMap,
- allAtomMatchers, allConditionTrackers, allMetricProducers,
+ if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
+ conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
conditionToMetricMap, trackerToMetricMap, metricProducerMap,
noReportMetricIds)) {
ALOGE("initMetricProducers failed");
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index d749bf4..c660149 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -23,7 +23,7 @@
#include "../anomaly/AlarmTracker.h"
#include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManagerImpl.h"
+#include "../external/StatsPullerManager.h"
#include "../matchers/LogMatchingTracker.h"
#include "../metrics/MetricProducer.h"
@@ -81,9 +81,8 @@
// the list of MetricProducer index
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
bool initMetrics(
- const ConfigKey& key, const StatsdConfig& config,
- const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
- UidMap& uidMap,
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+ const int64_t currentTimeNs, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager,
const std::unordered_map<int64_t, int>& logTrackerMap,
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
@@ -97,10 +96,10 @@
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
+ const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor,
- const int64_t timeBaseNs, const int64_t currentTimeNs,
- std::set<int>& allTagIds,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 07378db..4de9986 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -271,6 +271,7 @@
TEST(MetricsManagerTest, TestGoodConfig) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildGoodConfig();
@@ -285,13 +286,11 @@
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
EXPECT_EQ(1u, allMetricProducers.size());
EXPECT_EQ(1u, allAnomalyTrackers.size());
EXPECT_EQ(1u, noReportMetricIds.size());
@@ -299,6 +298,7 @@
TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildDimensionMetricsWithMultiTags();
@@ -313,17 +313,16 @@
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildCircleMatchers();
@@ -338,17 +337,16 @@
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildMissingMatchers();
@@ -362,17 +360,16 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildMissingPredicate();
@@ -386,17 +383,16 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildCirclePredicates();
@@ -411,17 +407,16 @@
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
UidMap uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildAlertWithUnknownMetric();
@@ -436,13 +431,11 @@
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
- anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers,
- allConditionTrackers, allMetricProducers, allAnomalyTrackers,
- allAlarmTrackers,
- conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
- noReportMetricIds));
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
+ periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
+ allAtomMatchers, allConditionTrackers, allMetricProducers,
+ allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
}
#else
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 76f3d81..ecc57f5 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -44,13 +44,13 @@
*/
class MockMetricsManager : public MetricsManager {
public:
- MockMetricsManager() : MetricsManager(
- ConfigKey(1, 12345), StatsdConfig(), 1000, 1000,
- new UidMap(),
- new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){}),
- new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){})) {
+ MockMetricsManager()
+ : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
+ new StatsPullerManager(),
+ new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
+ [](const sp<IStatsCompanionService>&) {}),
+ new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
+ [](const sp<IStatsCompanionService>&) {})) {
}
MOCK_METHOD0(byteSize, size_t());
@@ -60,11 +60,12 @@
TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
- [](const ConfigKey& key) {return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; });
MockMetricsManager mockMetricsManager;
@@ -79,11 +80,15 @@
TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) {
+ broadcastCount++;
+ return true;
+ });
MockMetricsManager mockMetricsManager;
@@ -105,11 +110,15 @@
TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) {
+ broadcastCount++;
+ return true;
+ });
MockMetricsManager mockMetricsManager;
@@ -143,12 +152,16 @@
TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
// Setup simple config key corresponding to empty config.
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) {
+ broadcastCount++;
+ return true;
+ });
ConfigKey key(3, 4);
StatsdConfig config = MakeConfig(true);
p.OnConfigUpdated(0, key, config);
@@ -168,12 +181,16 @@
TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) {
// Setup simple config key corresponding to empty config.
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) {
+ broadcastCount++;
+ return true;
+ });
ConfigKey key(3, 4);
StatsdConfig config = MakeConfig(false);
p.OnConfigUpdated(0, key, config);
@@ -191,11 +208,15 @@
TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) {
// Setup simple config key corresponding to empty config.
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) {
+ broadcastCount++;
+ return true;
+ });
ConfigKey key(3, 4);
StatsdConfig config;
auto annotation = config.add_annotation();
@@ -220,11 +241,15 @@
TEST(StatsLogProcessorTest, TestOutOfOrderLogs) {
// Setup simple config key corresponding to empty config.
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) {
+ broadcastCount++;
+ return true;
+ });
LogEvent event1(0, 1 /*logd timestamp*/, 1001 /*elapsedRealtime*/);
event1.init();
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index e23131d..99082cc 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -40,11 +40,12 @@
TEST(UidMapTest, TestIsolatedUID) {
sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [](const ConfigKey& key) {return true;});
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; });
LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
addEvent.write(100); // parent UID
addEvent.write(101); // isolated UID
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index eca5690..d98395e 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -66,7 +66,7 @@
baseTimeNs, configAddedTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mStatsPullerManager.ForceClearPullerCache();
+ processor->mPullerManager->ForceClearPullerCache();
int startBucketNum = processor->mMetricsManagers.begin()->second->
mAllMetricProducers[0]->getCurrentBucketNum();
@@ -74,12 +74,11 @@
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
- StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().intervalNs);
- int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().nextPullTimeNs;
+ processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+ int64_t& nextPullTimeNs =
+ processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
@@ -212,7 +211,7 @@
baseTimeNs, configAddedTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mStatsPullerManager.ForceClearPullerCache();
+ processor->mPullerManager->ForceClearPullerCache();
int startBucketNum = processor->mMetricsManagers.begin()->second->
mAllMetricProducers[0]->getCurrentBucketNum();
@@ -313,7 +312,7 @@
baseTimeNs, configAddedTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mStatsPullerManager.ForceClearPullerCache();
+ processor->mPullerManager->ForceClearPullerCache();
int startBucketNum = processor->mMetricsManagers.begin()->second->
mAllMetricProducers[0]->getCurrentBucketNum();
@@ -321,12 +320,11 @@
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
- StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().intervalNs);
- int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().nextPullTimeNs;
+ processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+ int64_t& nextPullTimeNs =
+ processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index dd28d36..744828e 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -66,7 +66,7 @@
baseTimeNs, configAddedTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mStatsPullerManager.ForceClearPullerCache();
+ processor->mPullerManager->ForceClearPullerCache();
int startBucketNum = processor->mMetricsManagers.begin()->second->
mAllMetricProducers[0]->getCurrentBucketNum();
@@ -74,12 +74,11 @@
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
- StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().intervalNs);
- int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().nextPullTimeNs;
+ processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+ int64_t& expectedPullTimeNs =
+ processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
@@ -173,7 +172,7 @@
baseTimeNs, configAddedTimeNs, config, cfgKey);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mStatsPullerManager.ForceClearPullerCache();
+ processor->mPullerManager->ForceClearPullerCache();
int startBucketNum = processor->mMetricsManagers.begin()->second->
mAllMetricProducers[0]->getCurrentBucketNum();
@@ -181,12 +180,11 @@
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+ EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
- StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().intervalNs);
- int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
- second.front().nextPullTimeNs;
+ processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
+ int64_t& expectedPullTimeNs =
+ processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
// Screen off/on/off events.
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 698ce72..c7e72f9 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -61,8 +61,7 @@
// TODO: pending refactor of StatsPullerManager
// For now we still need this so that it doesn't do real pulling.
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -89,8 +88,7 @@
EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
allData.clear();
- std::shared_ptr<LogEvent> event2 =
- std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
+ std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
event2->write(24);
event2->write("some value");
event2->write(25);
@@ -140,8 +138,7 @@
alert.set_trigger_if_sum_gt(25);
alert.set_num_buckets(100);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /* -1 means no pulling */, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
@@ -211,8 +208,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
@@ -280,8 +276,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
@@ -296,8 +291,8 @@
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -372,8 +367,7 @@
return ConditionState::kTrue;
}));
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
@@ -421,8 +415,7 @@
sp<AlarmMonitor> alarmMonitor;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -472,7 +465,7 @@
.mFields->begin()
->mValue.int_value);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
+ std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
std::shared_ptr<LogEvent> event3 =
std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
@@ -487,7 +480,7 @@
.mFields->begin()
->mValue.int_value);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
// The event4 does not have the gauge field. Thus the current bucket value is 0.
std::shared_ptr<LogEvent> event4 =
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index e3a8a55..d93b46f 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -62,8 +62,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
// TODO: pending refactor of StatsPullerManager
// For now we still need this so that it doesn't do real pulling.
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -140,8 +139,7 @@
metric.set_use_absolute_value_on_reset(true);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -214,8 +212,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -287,8 +284,7 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
@@ -365,8 +361,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
@@ -408,8 +403,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
@@ -464,8 +458,7 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
@@ -514,8 +507,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
@@ -556,8 +548,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
@@ -631,8 +622,10 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs);
+ -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
valueProducer.setBucketSize(60 * NS_PER_SEC);
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -705,8 +698,7 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -790,8 +782,7 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
@@ -873,8 +864,7 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
@@ -976,8 +966,7 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- shared_ptr<MockStatsPullerManager> pullerManager =
- make_shared<StrictMock<MockStatsPullerManager>>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e0c98cb..b8b1a1d 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -452,14 +452,16 @@
sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
const StatsdConfig& config, const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor =
new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
[](const sp<IStatsCompanionService>&){});
sp<AlarmMonitor> periodicAlarmMonitor =
new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
[](const sp<IStatsCompanionService>&){});
- sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){return true;});
+ sp<StatsLogProcessor> processor =
+ new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseNs, [](const ConfigKey&) { return true; });
processor->OnConfigUpdated(currentTimeNs, key, config);
return processor;
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index cc17ea8..1a3260f 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -137,6 +137,7 @@
Landroid/app/ActivityManager$StackInfo;->visible:Z
Landroid/app/ActivityManager$TaskDescription;->getBackgroundColor()I
Landroid/app/ActivityManager$TaskDescription;->getInMemoryIcon()Landroid/graphics/Bitmap;
+Landroid/app/ActivityManager$TaskDescription;->setIcon(Landroid/graphics/Bitmap;)V
Landroid/app/ActivityManager$TaskSnapshot;->getContentInsets()Landroid/graphics/Rect;
Landroid/app/ActivityManager$TaskSnapshot;->getOrientation()I
Landroid/app/ActivityManager$TaskSnapshot;->getScale()F
@@ -1217,6 +1218,7 @@
Landroid/bluetooth/IBluetooth$Stub;-><init>()V
Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
Landroid/bluetooth/IBluetooth$Stub;->TRANSACTION_enable:I
+Landroid/bluetooth/IBluetooth;->fetchRemoteUuids(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->isEnabled()Z
@@ -1491,6 +1493,7 @@
Landroid/content/pm/IPackageManager$Stub$Proxy;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstallLocation()I
+Landroid/content/pm/IPackageManager$Stub$Proxy;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
@@ -1721,6 +1724,7 @@
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
Landroid/content/pm/PackageParser;->generateActivityInfo(Landroid/content/pm/PackageParser$Activity;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/PackageParser;->generateApplicationInfo(Landroid/content/pm/PackageParser$Package;ILandroid/content/pm/PackageUserState;)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/PackageParser;->generateApplicationInfo(Landroid/content/pm/PackageParser$Package;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/PackageParser;->generateInstrumentationInfo(Landroid/content/pm/PackageParser$Instrumentation;I)Landroid/content/pm/InstrumentationInfo;
Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
@@ -1732,6 +1736,7 @@
Landroid/content/pm/PackageParser;->mCallback:Landroid/content/pm/PackageParser$Callback;
Landroid/content/pm/PackageParser;->NEW_PERMISSIONS:[Landroid/content/pm/PackageParser$NewPermissionInfo;
Landroid/content/pm/PackageParser;->parseBaseApk(Ljava/lang/String;Landroid/content/res/Resources;Landroid/content/res/XmlResourceParser;I[Ljava/lang/String;)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parseBaseApplication(Landroid/content/pm/PackageParser$Package;Landroid/content/res/Resources;Landroid/content/res/XmlResourceParser;I[Ljava/lang/String;)Z
Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
@@ -1815,6 +1820,7 @@
Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
Landroid/content/res/AssetManager;->sSystem:Landroid/content/res/AssetManager;
Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
+Landroid/content/res/ColorStateList;-><init>()V
Landroid/content/res/ColorStateList;->canApplyTheme()Z
Landroid/content/res/ColorStateList;->getColors()[I
Landroid/content/res/ColorStateList;->getStates()[[I
@@ -1877,6 +1883,7 @@
Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V
Landroid/content/res/ResourcesImpl;-><init>(Landroid/content/res/AssetManager;Landroid/util/DisplayMetrics;Landroid/content/res/Configuration;Landroid/view/DisplayAdjustments;)V
Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->getDisplayMetrics()Landroid/util/DisplayMetrics;
Landroid/content/res/ResourcesImpl;->getValue(ILandroid/util/TypedValue;Z)V
Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
@@ -2132,6 +2139,7 @@
Landroid/graphics/CanvasProperty;->createPaint(Landroid/graphics/Paint;)Landroid/graphics/CanvasProperty;
Landroid/graphics/ColorMatrixColorFilter;->mMatrix:Landroid/graphics/ColorMatrix;
Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
+Landroid/graphics/ColorMatrixColorFilter;->setColorMatrixArray([F)V
Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesCount(I)V
Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesDuration(I)V
@@ -4139,6 +4147,7 @@
Landroid/os/SELinux;->restoreconRecursive(Ljava/io/File;)Z
Landroid/os/ServiceManager;-><init>()V
Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
+Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;Z)V
Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;ZI)V
Landroid/os/ServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/ServiceManager;->getIServiceManager()Landroid/os/IServiceManager;
@@ -4223,6 +4232,7 @@
Landroid/os/storage/VolumeInfo;->getDiskId()Ljava/lang/String;
Landroid/os/storage/VolumeInfo;->getEnvironmentForState(I)Ljava/lang/String;
Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getInternalPath()Ljava/io/File;
Landroid/os/storage/VolumeInfo;->getInternalPathForUser(I)Ljava/io/File;
Landroid/os/storage/VolumeInfo;->getMountUserId()I
Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
@@ -4258,6 +4268,7 @@
Landroid/os/StrictMode;->sLastVmViolationTime:Ljava/util/HashMap;
Landroid/os/StrictMode;->sWindowManager:Landroid/util/Singleton;
Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
+Landroid/os/SystemClock;-><init>()V
Landroid/os/SystemClock;->currentThreadTimeMicro()J
Landroid/os/SystemClock;->currentTimeMicro()J
Landroid/os/SystemProperties;-><init>()V
@@ -4342,6 +4353,7 @@
Landroid/os/UserManager;->getUserStartRealtime()J
Landroid/os/UserManager;->getUserUnlockRealtime()J
Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
Landroid/os/UserManager;->isGuestUser(I)Z
Landroid/os/UserManager;->isLinkedUser()Z
Landroid/os/UserManager;->isUserAdmin(I)Z
@@ -4547,6 +4559,7 @@
Landroid/provider/Settings$Global;->ZEN_MODE_IMPORTANT_INTERRUPTIONS:I
Landroid/provider/Settings$Global;->ZEN_MODE_NO_INTERRUPTIONS:I
Landroid/provider/Settings$Global;->ZEN_MODE_OFF:I
+Landroid/provider/Settings$NameValueCache;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
Landroid/provider/Settings$NameValueCache;->mProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
Landroid/provider/Settings$Secure;->ACCESSIBILITY_AUTOCLICK_ENABLED:Ljava/lang/String;
Landroid/provider/Settings$Secure;->ACCESSIBILITY_CAPTIONING_TYPEFACE:Ljava/lang/String;
@@ -4585,10 +4598,12 @@
Landroid/provider/Settings$Secure;->SETTINGS_TO_BACKUP:[Ljava/lang/String;
Landroid/provider/Settings$Secure;->SMS_DEFAULT_APPLICATION:Ljava/lang/String;
Landroid/provider/Settings$Secure;->sNameValueCache:Landroid/provider/Settings$NameValueCache;
+Landroid/provider/Settings$Secure;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
Landroid/provider/Settings$Secure;->VOICE_RECOGNITION_SERVICE:Ljava/lang/String;
Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String;
Landroid/provider/Settings$System;->CAR_DOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->CAR_UNDOCK_SOUND:Ljava/lang/String;
+Landroid/provider/Settings$System;->CLONE_TO_MANAGED_PROFILE:Ljava/util/Set;
Landroid/provider/Settings$System;->DESK_DOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->DESK_UNDOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->DOCK_SOUNDS_ENABLED:Ljava/lang/String;
@@ -4600,6 +4615,9 @@
Landroid/provider/Settings$System;->LOCKSCREEN_SOUNDS_ENABLED:Ljava/lang/String;
Landroid/provider/Settings$System;->LOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->MASTER_MONO:Ljava/lang/String;
+Landroid/provider/Settings$System;->MOVED_TO_GLOBAL:Ljava/util/HashSet;
+Landroid/provider/Settings$System;->MOVED_TO_SECURE:Ljava/util/HashSet;
+Landroid/provider/Settings$System;->MOVED_TO_SECURE_THEN_GLOBAL:Ljava/util/HashSet;
Landroid/provider/Settings$System;->NOTIFICATION_LIGHT_PULSE:Ljava/lang/String;
Landroid/provider/Settings$System;->POINTER_LOCATION:Ljava/lang/String;
Landroid/provider/Settings$System;->POINTER_SPEED:Ljava/lang/String;
@@ -4614,6 +4632,7 @@
Landroid/provider/Settings$System;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
Landroid/provider/Settings$System;->TTY_MODE:Ljava/lang/String;
Landroid/provider/Settings$System;->UNLOCK_SOUND:Ljava/lang/String;
+Landroid/provider/Settings$System;->VALIDATORS:Ljava/util/Map;
Landroid/provider/Settings$System;->VIBRATE_IN_SILENT:Ljava/lang/String;
Landroid/provider/Settings;->ACTION_TRUSTED_CREDENTIALS_USER:Ljava/lang/String;
Landroid/provider/Settings;->ACTION_USER_DICTIONARY_INSERT:Ljava/lang/String;
@@ -5357,6 +5376,7 @@
Landroid/telephony/ServiceState;->mCdmaRoamingIndicator:I
Landroid/telephony/ServiceState;->mCssIndicator:Z
Landroid/telephony/ServiceState;->mIsManualNetworkSelection:Z
+Landroid/telephony/ServiceState;->mIsUsingCarrierAggregation:Z
Landroid/telephony/ServiceState;->mNetworkId:I
Landroid/telephony/ServiceState;->mSystemId:I
Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
@@ -5720,6 +5740,7 @@
Landroid/transition/Scene;->setCurrentScene(Landroid/view/View;Landroid/transition/Scene;)V
Landroid/transition/Transition;->cancel()V
Landroid/transition/Transition;->end()V
+Landroid/transition/Transition;->getRunningAnimators()Landroid/util/ArrayMap;
Landroid/transition/TransitionManager;->getRunningTransitions()Landroid/util/ArrayMap;
Landroid/transition/TransitionManager;->sPendingTransitions:Ljava/util/ArrayList;
Landroid/transition/TransitionManager;->sRunningTransitions:Ljava/lang/ThreadLocal;
@@ -6166,6 +6187,7 @@
Landroid/view/SurfaceControl;->HIDDEN:I
Landroid/view/SurfaceControl;->hide()V
Landroid/view/SurfaceControl;->openTransaction()V
+Landroid/view/SurfaceControl;->screenshot(Landroid/graphics/Rect;III)Landroid/graphics/Bitmap;
Landroid/view/SurfaceControl;->screenshot(Landroid/graphics/Rect;IIIIZI)Landroid/graphics/Bitmap;
Landroid/view/SurfaceControl;->screenshot(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V
Landroid/view/SurfaceControl;->setDisplayLayerStack(Landroid/os/IBinder;I)V
@@ -6959,6 +6981,7 @@
Landroid/widget/ListView;->fillSpecific(II)Landroid/view/View;
Landroid/widget/ListView;->fillUp(II)Landroid/view/View;
Landroid/widget/ListView;->getHeightForPosition(I)I
+Landroid/widget/ListView;->isDirectChildHeaderOrFooter(Landroid/view/View;)Z
Landroid/widget/ListView;->makeAndAddView(IIZIZ)Landroid/view/View;
Landroid/widget/ListView;->mAreAllItemsSelectable:Z
Landroid/widget/ListView;->mDivider:Landroid/graphics/drawable/Drawable;
@@ -7507,6 +7530,7 @@
Lcom/android/internal/appwidget/IAppWidgetService;->bindAppWidgetId(Ljava/lang/String;IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
Lcom/android/internal/appwidget/IAppWidgetService;->bindRemoteViewsService(Ljava/lang/String;ILandroid/content/Intent;Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/app/IServiceConnection;I)Z
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
+Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
Lcom/android/internal/backup/IBackupTransport$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/backup/IBackupTransport;
Lcom/android/internal/backup/IBackupTransport;->clearBackupData(Landroid/content/pm/PackageInfo;)I
@@ -7723,6 +7747,9 @@
Lcom/android/internal/R$attr;->text:I
Lcom/android/internal/R$attr;->title:I
Lcom/android/internal/R$attr;->webViewStyle:I
+Lcom/android/internal/R$bool;-><init>()V
+Lcom/android/internal/R$bool;->config_automatic_brightness_available:I
+Lcom/android/internal/R$bool;->config_intrusiveNotificationLed:I
Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I
Lcom/android/internal/R$bool;->config_showNavigationBar:I
Lcom/android/internal/R$dimen;-><init>()V
@@ -8146,6 +8173,8 @@
Lcom/android/internal/R$styleable;->Window:[I
Lcom/android/internal/R$styleable;->WindowAnimation:[I
Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$styleable;->Window_windowBackground:I
+Lcom/android/internal/R$styleable;->Window_windowFullscreen:I
Lcom/android/internal/R$styleable;->Window_windowIsFloating:I
Lcom/android/internal/R$styleable;->Window_windowIsTranslucent:I
Lcom/android/internal/R$styleable;->Window_windowShowWallpaper:I
@@ -8215,6 +8244,7 @@
Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthChanged(I)V
Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthsChanged(Landroid/telephony/SignalStrength;)V
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;-><init>()V
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
@@ -8400,6 +8430,8 @@
Lcom/android/internal/view/menu/MenuBuilder;->addMenuPresenter(Lcom/android/internal/view/menu/MenuPresenter;Landroid/content/Context;)V
Lcom/android/internal/view/menu/MenuBuilder;->collapseItemActionView(Lcom/android/internal/view/menu/MenuItemImpl;)Z
Lcom/android/internal/view/menu/MenuBuilder;->getContext()Landroid/content/Context;
+Lcom/android/internal/view/menu/MenuBuilder;->getHeaderIcon()Landroid/graphics/drawable/Drawable;
+Lcom/android/internal/view/menu/MenuBuilder;->getHeaderTitle()Ljava/lang/CharSequence;
Lcom/android/internal/view/menu/MenuBuilder;->getNonActionItems()Ljava/util/ArrayList;
Lcom/android/internal/view/menu/MenuBuilder;->getRootMenu()Lcom/android/internal/view/menu/MenuBuilder;
Lcom/android/internal/view/menu/MenuBuilder;->getVisibleItems()Ljava/util/ArrayList;
@@ -8417,11 +8449,14 @@
Lcom/android/internal/view/menu/MenuItemImpl;->requestsActionButton()Z
Lcom/android/internal/view/menu/MenuItemImpl;->requiresActionButton()Z
Lcom/android/internal/view/menu/MenuItemImpl;->setActionViewExpanded(Z)V
+Lcom/android/internal/view/menu/MenuItemImpl;->setExclusiveCheckable(Z)V
Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
Lcom/android/internal/view/menu/MenuPopupHelper;-><init>(Landroid/content/Context;Lcom/android/internal/view/menu/MenuBuilder;)V
Lcom/android/internal/view/menu/MenuPopupHelper;-><init>(Landroid/content/Context;Lcom/android/internal/view/menu/MenuBuilder;Landroid/view/View;)V
Lcom/android/internal/view/menu/MenuPopupHelper;->dismiss()V
+Lcom/android/internal/view/menu/MenuPopupHelper;->getPopup()Lcom/android/internal/view/menu/MenuPopup;
Lcom/android/internal/view/menu/MenuPopupHelper;->mForceShowIcon:Z
+Lcom/android/internal/view/menu/MenuPopupHelper;->setAnchorView(Landroid/view/View;)V
Lcom/android/internal/view/menu/MenuPopupHelper;->setForceShowIcon(Z)V
Lcom/android/internal/view/menu/MenuPopupHelper;->setGravity(I)V
Lcom/android/internal/view/menu/MenuPopupHelper;->show()V
@@ -8751,6 +8786,7 @@
Ljava/lang/reflect/Field;->getOffset()I
Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/Runtime;-><init>()V
Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
Ljava/lang/Runtime;->loadLibrary0(Ljava/lang/ClassLoader;Ljava/lang/String;)V
@@ -9119,6 +9155,7 @@
Lorg/xml/sax/helpers/NamespaceSupport;->contexts:[Lorg/xml/sax/helpers/NamespaceSupport$Context;
Lorg/xml/sax/helpers/NamespaceSupport;->currentContext:Lorg/xml/sax/helpers/NamespaceSupport$Context;
Lorg/xml/sax/helpers/NamespaceSupport;->EMPTY_ENUMERATION:Ljava/util/Enumeration;
+Lorg/xml/sax/helpers/NamespaceSupport;->namespaceDeclUris:Z
Lorg/xml/sax/helpers/ParserAdapter;->attAdapter:Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;
Lorg/xml/sax/helpers/ParserAdapter;->atts:Lorg/xml/sax/helpers/AttributesImpl;
Lorg/xml/sax/helpers/ParserAdapter;->checkNotParsing(Ljava/lang/String;Ljava/lang/String;)V
@@ -9138,6 +9175,7 @@
Lorg/xml/sax/helpers/ParserAdapter;->reportError(Ljava/lang/String;)V
Lorg/xml/sax/helpers/ParserAdapter;->setup(Lorg/xml/sax/Parser;)V
Lorg/xml/sax/helpers/ParserAdapter;->setupParser()V
+Lorg/xml/sax/helpers/ParserAdapter;->uris:Z
Lorg/xml/sax/helpers/XMLFilterImpl;->contentHandler:Lorg/xml/sax/ContentHandler;
Lorg/xml/sax/helpers/XMLFilterImpl;->dtdHandler:Lorg/xml/sax/DTDHandler;
Lorg/xml/sax/helpers/XMLFilterImpl;->entityResolver:Lorg/xml/sax/EntityResolver;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e08b54f..f27b286 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4048,7 +4048,7 @@
*/
public static void setVrThread(int tid) {
try {
- getService().setVrThread(tid);
+ getTaskService().setVrThread(tid);
} catch (RemoteException e) {
// pass
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1d5f49a..e5cfe84 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
@@ -31,6 +32,7 @@
import com.android.internal.app.IVoiceInteractor;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -40,61 +42,11 @@
*/
public abstract class ActivityManagerInternal {
- /**
- * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
- * the splash screen.
- */
- public static final int APP_TRANSITION_SPLASH_SCREEN =
- AppProtoEnums.APP_TRANSITION_SPLASH_SCREEN; // 1
- /**
- * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
- * app windows were drawn
- */
- public static final int APP_TRANSITION_WINDOWS_DRAWN =
- AppProtoEnums.APP_TRANSITION_WINDOWS_DRAWN; // 2
-
- /**
- * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
- * timeout.
- */
- public static final int APP_TRANSITION_TIMEOUT =
- AppProtoEnums.APP_TRANSITION_TIMEOUT; // 3
-
- /**
- * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
- * we drew a task snapshot.
- */
- public static final int APP_TRANSITION_SNAPSHOT =
- AppProtoEnums.APP_TRANSITION_SNAPSHOT; // 4
-
- /**
- * Type for {@link #notifyAppTransitionStarting}: The transition was started because it was a
- * recents animation and we only needed to wait on the wallpaper.
- */
- public static final int APP_TRANSITION_RECENTS_ANIM =
- AppProtoEnums.APP_TRANSITION_RECENTS_ANIM; // 5
-
- /**
- * The bundle key to extract the assist data.
- */
- public static final String ASSIST_KEY_DATA = "data";
-
- /**
- * The bundle key to extract the assist structure.
- */
- public static final String ASSIST_KEY_STRUCTURE = "structure";
-
- /**
- * The bundle key to extract the assist content.
- */
- public static final String ASSIST_KEY_CONTENT = "content";
-
- /**
- * The bundle key to extract the assist receiver extras.
- */
- public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
-
+ // Access modes for handleIncomingUser.
+ public static final int ALLOW_NON_FULL = 0;
+ public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+ public static final int ALLOW_FULL_ONLY = 2;
/**
* Grant Uri permissions from one app to another. This method only extends
@@ -118,81 +70,6 @@
String processName, String abiOverride, int uid, Runnable crashHandler);
/**
- * Acquires a sleep token for the specified display with the specified tag.
- *
- * @param tag A string identifying the purpose of the token (eg. "Dream").
- * @param displayId The display to apply the sleep token to.
- */
- public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
-
- /**
- * Sleep tokens cause the activity manager to put the top activity to sleep.
- * They are used by components such as dreams that may hide and block interaction
- * with underlying activities.
- */
- public static abstract class SleepToken {
-
- /**
- * Releases the sleep token.
- */
- public abstract void release();
- }
-
- /**
- * Returns home activity for the specified user.
- *
- * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
- */
- public abstract ComponentName getHomeActivityForUser(int userId);
-
- /**
- * Called when a user has been deleted. This can happen during normal device usage
- * or just at startup, when partially removed users are purged. Any state persisted by the
- * ActivityManager should be purged now.
- *
- * @param userId The user being cleaned up.
- */
- public abstract void onUserRemoved(int userId);
-
- public abstract void onLocalVoiceInteractionStarted(IBinder callingActivity,
- IVoiceInteractionSession mSession,
- IVoiceInteractor mInteractor);
-
- /**
- * Callback for window manager to let activity manager know that we are finally starting the
- * app transition;
- *
- * @param reasons A map from windowing mode to a reason integer why the transition was started,
- * which must be one of the APP_TRANSITION_* values.
- * @param timestamp The time at which the app transition started in
- * {@link SystemClock#uptimeMillis()} timebase.
- */
- public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
-
- /**
- * Callback for window manager to let activity manager know that the app transition was
- * cancelled.
- */
- public abstract void notifyAppTransitionCancelled();
-
- /**
- * Callback for window manager to let activity manager know that the app transition is finished.
- */
- public abstract void notifyAppTransitionFinished();
-
- /**
- * Returns the top activity from each of the currently visible stacks. The first entry will be
- * the focused activity.
- */
- public abstract List<IBinder> getTopVisibleActivities();
-
- /**
- * Callback for window manager to let activity manager know that docked stack changes its
- * minimized state.
- */
- public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
-
- /**
* Kill foreground apps from the specified user.
*/
public abstract void killForegroundAppsForUser(int userHandle);
@@ -216,37 +93,6 @@
boolean adding);
/**
- * Updates and persists the {@link Configuration} for a given user.
- *
- * @param values the configuration to update
- * @param userId the user to update the configuration for
- */
- public abstract void updatePersistentConfigurationForUser(@NonNull Configuration values,
- int userId);
-
- /**
- * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
- *
- * - DO NOT call it with the calling UID cleared.
- * - All the necessary caller permission checks must be done at callsites.
- *
- * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
- */
- public abstract int startActivitiesAsPackage(String packageName,
- int userId, Intent[] intents, Bundle bOptions);
-
- /**
- * Start activity {@code intent} without calling user-id check.
- *
- * - DO NOT call it with the calling UID cleared.
- * - The caller must do the calling user ID check.
- *
- * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
- */
- public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
- Intent intent, @Nullable Bundle options, int userId);
-
- /**
* Get the procstate for the UID. The return value will be between
* {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}.
* Note if the UID doesn't exist, it'll return {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
@@ -255,25 +101,11 @@
public abstract int getUidProcessState(int uid);
/**
- * Called when Keyguard flags might have changed.
- *
- * @param callback Callback to run after activity visibilities have been reevaluated. This can
- * be used from window manager so that when the callback is called, it's
- * guaranteed that all apps have their visibility updated accordingly.
- */
- public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback);
-
- /**
* @return {@code true} if system is ready, {@code false} otherwise.
*/
public abstract boolean isSystemReady();
/**
- * Called when the trusted state of Keyguard has changed.
- */
- public abstract void notifyKeyguardTrustedChanged();
-
- /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
@@ -301,18 +133,6 @@
public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
/**
- * Called after the voice interaction service has changed.
- */
- public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
-
- /**
- * Called after virtual display Id is updated by
- * {@link com.android.server.vr.Vr2dDisplay} with a specific
- * {@param vr2dDisplayId}.
- */
- public abstract void setVr2dDisplayId(int vr2dDisplayId);
-
- /**
* Saves the current activity manager state and includes the saved state in the next dump of
* activity manager.
*/
@@ -324,40 +144,11 @@
public abstract void clearSavedANRState();
/**
- * Set focus on an activity.
- * @param token The IApplicationToken for the activity
- */
- public abstract void setFocusedActivity(IBinder token);
-
- /**
- * Set a uid that is allowed to bypass stopped app switches, launching an app
- * whenever it wants.
- *
- * @param type Type of the caller -- unique string the caller supplies to identify itself
- * and disambiguate with other calles.
- * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type.
- * @param userId The user it is allowed for.
- */
- public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
-
- /**
* @return true if runtime was restarted, false if it's normal boot
*/
public abstract boolean isRuntimeRestarted();
/**
- * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
- */
- public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
-
- public interface ScreenObserver {
- public void onAwakeStateChanged(boolean isAwake);
- public void onKeyguardStateChanged(boolean isShowing);
- }
-
- public abstract void registerScreenObserver(ScreenObserver observer);
-
- /**
* Returns if more users can be started without stopping currently running users.
*/
public abstract boolean canStartMoreUsers();
@@ -378,21 +169,6 @@
public abstract int getMaxRunningUsers();
/**
- * Returns is the caller has the same uid as the Recents component
- */
- public abstract boolean isCallerRecents(int callingUid);
-
- /**
- * Returns whether the recents component is the home activity for the given user.
- */
- public abstract boolean isRecentsComponentHomeActivity(int userId);
-
- /**
- * Cancels any currently running recents animation.
- */
- public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
-
- /**
* Whether an UID is active or idle.
*/
public abstract boolean isUidActive(int uid);
@@ -403,8 +179,38 @@
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
/**
- * This enforces {@code func} can only be called if either the caller is Recents activity or
- * has {@code permission}.
+ * Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
+ * needed.
*/
- public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
+ public abstract int handleIncomingUser(int callingPid, int callingUid, int userId,
+ boolean allowAll, int allowMode, String name, String callerPackage);
+
+ /** Checks if the calling binder pid as the permission. */
+ public abstract void enforceCallingPermission(String permission, String func);
+
+ /** Returns the current user id. */
+ public abstract int getCurrentUserId();
+
+ /** Returns true if the user is running. */
+ public abstract boolean isUserRunning(int userId, int flags);
+
+ /** Trims memory usage in the system by removing/stopping unused application processes. */
+ public abstract void trimApplications();
+
+ /** Returns the screen compatibility mode for the given application. */
+ public abstract int getPackageScreenCompatMode(ApplicationInfo ai);
+
+ /** Sets the screen compatibility mode for the given application. */
+ public abstract void setPackageScreenCompatMode(ApplicationInfo ai, int mode);
+
+ /** Closes all system dialogs. */
+ public abstract void closeSystemDialogs(String reason);
+
+ /** Kill the processes in the list due to their tasks been removed. */
+ public abstract void killProcessesForRemovedTask(ArrayList<Object> procsToKill);
+
+ /**
+ * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
+ */
+ public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8639849..090e584 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -350,8 +350,10 @@
public static final int OP_START_FOREGROUND = 76;
/** @hide */
public static final int OP_BLUETOOTH_SCAN = 77;
+ /** @hide Use the face authentication API. */
+ public static final int OP_USE_FACE = 78;
/** @hide */
- public static final int _NUM_OP = 78;
+ public static final int _NUM_OP = 79;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -596,6 +598,11 @@
/** @hide */
public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
+ /** @hide Use the face authentication API. */
+ public static final String OPSTR_USE_FACE = "android:use_FACE";
+
+
+
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -733,6 +740,7 @@
OP_MANAGE_IPSEC_TUNNELS, // MANAGE_IPSEC_HANDOVERS
OP_START_FOREGROUND, // START_FOREGROUND
OP_COARSE_LOCATION, // BLUETOOTH_SCAN
+ OP_USE_FACE, // FACE
};
/**
@@ -817,6 +825,7 @@
OPSTR_MANAGE_IPSEC_TUNNELS,
OPSTR_START_FOREGROUND,
OPSTR_BLUETOOTH_SCAN,
+ OPSTR_USE_FACE,
};
/**
@@ -902,6 +911,7 @@
"MANAGE_IPSEC_TUNNELS",
"START_FOREGROUND",
"BLUETOOTH_SCAN",
+ "USE_FACE",
};
/**
@@ -987,6 +997,7 @@
null, // no permission for OP_MANAGE_IPSEC_TUNNELS
Manifest.permission.FOREGROUND_SERVICE,
null, // no permission for OP_BLUETOOTH_SCAN
+ Manifest.permission.USE_BIOMETRIC,
};
/**
@@ -1073,6 +1084,7 @@
null, // MANAGE_IPSEC_TUNNELS
null, // START_FOREGROUND
null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
+ null, // USE_FACE
};
/**
@@ -1158,6 +1170,7 @@
false, // MANAGE_IPSEC_HANDOVERS
false, // START_FOREGROUND
true, // BLUETOOTH_SCAN
+ false, // USE_FACE
};
/**
@@ -1242,6 +1255,7 @@
AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
AppOpsManager.MODE_ALLOWED, // OP_START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_SCAN
+ AppOpsManager.MODE_ALLOWED, // USE_FACE
};
/**
@@ -1330,6 +1344,7 @@
false, // MANAGE_IPSEC_TUNNELS
false, // START_FOREGROUND
false, // BLUETOOTH_SCAN
+ false, // USE_FACE
};
/**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8aae406..b39eb9b 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -465,7 +465,6 @@
boolean isBackgroundRestricted(in String packageName);
// Start of N MR1 transactions
- void setVrThread(int tid);
void setRenderThread(int tid);
/**
* Lets activity manager know whether the calling process is currently showing "top-level" UI
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index d2c6402..1eb187e 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -415,4 +415,10 @@
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
void alwaysShowUnsupportedCompileSdkWarning(in ComponentName activity);
+
+ void setVrThread(int tid);
+ void setPersistentVrThread(int tid);
+ void stopAppSwitches();
+ void resumeAppSwitches();
+ void setActivityController(in IActivityController watcher, boolean imAMonkey);
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ba355f9..1ad3054 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -644,7 +644,7 @@
@Nullable
private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
- if (uri == null) {
+ if (uri == null || Uri.EMPTY.equals(uri)) {
return null;
}
ContentResolver contentResolver = context.getContentResolver();
@@ -680,7 +680,7 @@
private Uri getSoundForBackup(Context context) {
Uri sound = getSound();
- if (sound == null) {
+ if (sound == null || Uri.EMPTY.equals(sound)) {
return null;
}
Uri canonicalSound = context.getContentResolver().canonicalize(sound);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e5f143c..98dc237 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -54,6 +54,8 @@
import android.hardware.SystemSensorManager;
import android.hardware.camera2.CameraManager;
import android.hardware.display.DisplayManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.hdmi.HdmiControlManager;
@@ -791,6 +793,22 @@
return new FingerprintManager(ctx.getOuterContext(), service);
}});
+ registerService(Context.FACE_SERVICE, FaceManager.class,
+ new CachedServiceFetcher<FaceManager>() {
+ @Override
+ public FaceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ final IBinder binder;
+ if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+ binder = ServiceManager.getServiceOrThrow(Context.FACE_SERVICE);
+ } else {
+ binder = ServiceManager.getService(Context.FACE_SERVICE);
+ }
+ IFaceService service = IFaceService.Stub.asInterface(binder);
+ return new FaceManager(ctx.getOuterContext(), service);
+ }
+ });
+
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
new CachedServiceFetcher<TvInputManager>() {
@Override
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index fde756c..6ad6c25 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -996,17 +996,29 @@
}
/**
- * If the current wallpaper is a live wallpaper component, return the
- * information about that wallpaper. Otherwise, if it is a static image,
- * simply return null.
+ * Returns the information about the wallpaper if the current wallpaper is
+ * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+ * this returns null.
*/
public WallpaperInfo getWallpaperInfo() {
+ return getWallpaperInfo(mContext.getUserId());
+ }
+
+ /**
+ * Returns the information about the wallpaper if the current wallpaper is
+ * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+ * this returns null.
+ *
+ * @param userId Owner of the wallpaper.
+ * @hide
+ */
+ public WallpaperInfo getWallpaperInfo(int userId) {
try {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- return sGlobals.mService.getWallpaperInfo(mContext.getUserId());
+ return sGlobals.mService.getWallpaperInfo(userId);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 21d6762..7a729f9 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -124,6 +124,24 @@
})
public @interface ActivityType {}
+ /** The current always on top status of the configuration. */
+ private @AlwaysOnTop int mAlwaysOnTop;
+
+ /** Always on top is currently not defined. */
+ private static final int ALWAYS_ON_TOP_UNDEFINED = 0;
+ /** Always on top is currently on for this configuration. */
+ private static final int ALWAYS_ON_TOP_ON = 1;
+ /** Always on top is currently off for this configuration. */
+ private static final int ALWAYS_ON_TOP_OFF = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = {
+ ALWAYS_ON_TOP_UNDEFINED,
+ ALWAYS_ON_TOP_ON,
+ ALWAYS_ON_TOP_OFF,
+ })
+ private @interface AlwaysOnTop {}
+
/** Bit that indicates that the {@link #mBounds} changed.
* @hide */
public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
@@ -136,13 +154,16 @@
/** Bit that indicates that the {@link #mActivityType} changed.
* @hide */
public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
-
+ /** Bit that indicates that the {@link #mAlwaysOnTop} changed.
+ * @hide */
+ public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 4;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
WINDOW_CONFIG_BOUNDS,
WINDOW_CONFIG_APP_BOUNDS,
WINDOW_CONFIG_WINDOWING_MODE,
- WINDOW_CONFIG_ACTIVITY_TYPE
+ WINDOW_CONFIG_ACTIVITY_TYPE,
+ WINDOW_CONFIG_ALWAYS_ON_TOP,
})
public @interface WindowConfig {}
@@ -168,6 +189,7 @@
dest.writeParcelable(mAppBounds, flags);
dest.writeInt(mWindowingMode);
dest.writeInt(mActivityType);
+ dest.writeInt(mAlwaysOnTop);
}
private void readFromParcel(Parcel source) {
@@ -175,6 +197,7 @@
mAppBounds = source.readParcelable(Rect.class.getClassLoader());
mWindowingMode = source.readInt();
mActivityType = source.readInt();
+ mAlwaysOnTop = source.readInt();
}
@Override
@@ -222,6 +245,21 @@
setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
}
+
+
+ /**
+ * Sets whether this window should be always on top.
+ * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
+ * @hide
+ */
+ public void setAlwaysOnTop(boolean alwaysOnTop) {
+ mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF;
+ }
+
+ private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) {
+ mAlwaysOnTop = alwaysOnTop;
+ }
+
/**
* @see #setAppBounds(Rect)
* @see #getAppBounds()
@@ -281,6 +319,7 @@
setAppBounds(other.mAppBounds);
setWindowingMode(other.mWindowingMode);
setActivityType(other.mActivityType);
+ setAlwaysOnTop(other.mAlwaysOnTop);
}
/** Set this object to completely undefined.
@@ -295,6 +334,7 @@
setBounds(null);
setWindowingMode(WINDOWING_MODE_UNDEFINED);
setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
}
/**
@@ -326,6 +366,11 @@
changed |= WINDOW_CONFIG_ACTIVITY_TYPE;
setActivityType(delta.mActivityType);
}
+ if (delta.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED
+ && mAlwaysOnTop != delta.mAlwaysOnTop) {
+ changed |= WINDOW_CONFIG_ALWAYS_ON_TOP;
+ setAlwaysOnTop(delta.mAlwaysOnTop);
+ }
return changed;
}
@@ -364,6 +409,11 @@
changes |= WINDOW_CONFIG_ACTIVITY_TYPE;
}
+ if ((compareUndefined || other.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED)
+ && mAlwaysOnTop != other.mAlwaysOnTop) {
+ changes |= WINDOW_CONFIG_ALWAYS_ON_TOP;
+ }
+
return changes;
}
@@ -398,6 +448,8 @@
if (n != 0) return n;
n = mActivityType - that.mActivityType;
if (n != 0) return n;
+ n = mAlwaysOnTop - that.mAlwaysOnTop;
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -425,6 +477,7 @@
result = 31 * result + mWindowingMode;
result = 31 * result + mActivityType;
+ result = 31 * result + mAlwaysOnTop;
return result;
}
@@ -434,7 +487,9 @@
return "{ mBounds=" + mBounds
+ " mAppBounds=" + mAppBounds
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
- + " mActivityType=" + activityTypeToString(mActivityType) + "}";
+ + " mActivityType=" + activityTypeToString(mActivityType)
+ + " mAlwaysOnTop=" + activityTypeToString(mAlwaysOnTop)
+ + "}";
}
/**
@@ -520,7 +575,7 @@
* @hide
*/
public boolean isAlwaysOnTop() {
- return mWindowingMode == WINDOWING_MODE_PINNED;
+ return mWindowingMode == WINDOWING_MODE_PINNED || mAlwaysOnTop == ALWAYS_ON_TOP_ON;
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ff38c1f..5e7f1e4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3237,8 +3237,8 @@
/**
* Called by a device/profile owner to set the timeout after which unlocking with secondary, non
- * strong auth (e.g. fingerprint, trust agents) times out, i.e. the user has to use a strong
- * authentication method like password, pin or pattern.
+ * strong auth (e.g. fingerprint, face, trust agents) times out, i.e. the user has to use a
+ * strong authentication method like password, pin or pattern.
*
* <p>This timeout is used internally to reset the timer to require strong auth again after
* specified timeout each time it has been successfully used.
@@ -3710,7 +3710,6 @@
| DevicePolicyManager.KEYGUARD_DISABLE_IRIS
| DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
-
/**
* Disable all current and future keyguard customizations.
*/
@@ -4898,10 +4897,10 @@
/**
* @hide
*/
- public void reportFailedFingerprintAttempt(int userHandle) {
+ public void reportFailedBiometricAttempt(int userHandle) {
if (mService != null) {
try {
- mService.reportFailedFingerprintAttempt(userHandle);
+ mService.reportFailedBiometricAttempt(userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4911,10 +4910,10 @@
/**
* @hide
*/
- public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ public void reportSuccessfulBiometricAttempt(int userHandle) {
if (mService != null) {
try {
- mService.reportSuccessfulFingerprintAttempt(userHandle);
+ mService.reportSuccessfulBiometricAttempt(userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8300,6 +8299,22 @@
}
/**
+ * Makes all accumulated network logs available to DPC in a new batch.
+ * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+ * @hide
+ */
+ public long forceNetworkLogs() {
+ if (mService == null) {
+ return -1;
+ }
+ try {
+ return mService.forceNetworkLogs();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Forces a batch of security logs to be fetched from logd and makes it available for DPC.
* Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
* @hide
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 37508cd..c95bc5b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,8 +132,8 @@
void reportPasswordChanged(int userId);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
- void reportFailedFingerprintAttempt(int userHandle);
- void reportSuccessfulFingerprintAttempt(int userHandle);
+ void reportFailedBiometricAttempt(int userHandle);
+ void reportSuccessfulBiometricAttempt(int userHandle);
void reportKeyguardDismissed(int userHandle);
void reportKeyguardSecured(int userHandle);
@@ -347,6 +347,7 @@
boolean isSecurityLoggingEnabled(in ComponentName admin);
ParceledListSlice retrieveSecurityLogs(in ComponentName admin);
ParceledListSlice retrievePreRebootSecurityLogs(in ComponentName admin);
+ long forceNetworkLogs();
long forceSecurityLogs();
boolean isUninstallInQueue(String packageName);
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 6d65e3e..9985cc0 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -17,6 +17,7 @@
package android.app.trust;
import android.app.trust.ITrustListener;
+import android.hardware.biometrics.BiometricSourceType;
/**
* System private API to comunicate with trust service.
@@ -34,6 +35,6 @@
boolean isDeviceLocked(int userId);
boolean isDeviceSecure(int userId);
boolean isTrustUsuallyManaged(int userId);
- void unlockedByFingerprintForUser(int userId);
- void clearAllFingerprints();
+ void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
+ void clearAllBiometricRecognized(in BiometricSourceType target);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 8ab0b70..fb27bed 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -20,6 +20,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -195,26 +196,28 @@
}
/**
- * Updates the trust state for the user due to the user unlocking via fingerprint.
- * Should only be called if user authenticated via fingerprint and bouncer can be skipped.
+ * Updates the trust state for the user due to the user unlocking via a biometric sensor.
+ * Should only be called if user authenticated via fingerprint, face, or iris and bouncer
+ * can be skipped.
+ *
* @param userId
*/
@RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
- public void unlockedByFingerprintForUser(int userId) {
+ public void unlockedByBiometricForUser(int userId, BiometricSourceType source) {
try {
- mService.unlockedByFingerprintForUser(userId);
+ mService.unlockedByBiometricForUser(userId, source);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Clears authenticated fingerprints for all users.
+ * Clears authentication by the specified biometric type for all users.
*/
@RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
- public void clearAllFingerprints() {
+ public void clearAllBiometricRecognized(BiometricSourceType source) {
try {
- mService.clearAllFingerprints();
+ mService.clearAllBiometricRecognized(source);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c0cfb90..5e96b92 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3005,6 +3005,7 @@
NSD_SERVICE,
AUDIO_SERVICE,
FINGERPRINT_SERVICE,
+ //@hide: FACE_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -3652,6 +3653,18 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.hardware.face.FaceManager} for handling management
+ * of face authentication.
+ *
+ * @hide
+ * @see #getSystemService
+ * @see android.hardware.face.FaceManager
+ */
+ public static final String FACE_SERVICE = "face";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 43b6984..a76bc3a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2245,12 +2245,20 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
- */
+ */
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FACE = "android.hardware.face";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports portrait orientation
* screens. For backwards compatibility, you can assume that if neither
* this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 950070e..033d2ee 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2206,10 +2206,10 @@
com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- targetCode = minCode = val.string.toString();
+ minCode = val.string.toString();
} else {
// If it's not a string, it's an integer.
- targetVers = minVers = val.data;
+ minVers = val.data;
}
}
@@ -2225,6 +2225,9 @@
// If it's not a string, it's an integer.
targetVers = val.data;
}
+ } else {
+ targetVers = minVers;
+ targetCode = minCode;
}
sa.recycle();
@@ -3097,6 +3100,14 @@
0);
perm.info.requestRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
+ perm.info.requestDetailResourceId = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+ perm.info.backgroundRequestResourceId = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
+ 0);
+ perm.info.backgroundRequestDetailResourceId = sa.getResourceId(
+ com.android.internal.R.styleable
+ .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
perm.info.priority = sa.getInt(
@@ -3151,6 +3162,19 @@
perm.info.requestRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermission_request, 0);
+ if (sa.hasValue(
+ com.android.internal.R.styleable.AndroidManifestPermission_backgroundPermission)) {
+ if ("android".equals(owner.packageName)) {
+ perm.info.backgroundPermission = sa.getNonResourceString(
+ com.android.internal.R.styleable
+ .AndroidManifestPermission_backgroundPermission);
+ } else {
+ Slog.w(TAG, owner.packageName + " defines permission '" + perm.info.name
+ + "' with a background permission. Only the 'android' package can do "
+ + "that.");
+ }
+ }
+
perm.info.protectionLevel = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
PermissionInfo.PROTECTION_NORMAL);
diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java
index 7c4478d..8cf66d8 100644
--- a/core/java/android/content/pm/PermissionGroupInfo.java
+++ b/core/java/android/content/pm/PermissionGroupInfo.java
@@ -45,6 +45,42 @@
public @StringRes int requestRes;
/**
+ * A string resource identifier (in the package's resources) used as subtitle when requesting
+ * only access while in the foreground.
+ *
+ * From the "requestDetail" attribute or, if not set, {@link
+ * android.content.res.ResourceId#ID_NULL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int requestDetailResourceId;
+
+ /**
+ * A string resource identifier (in the package's resources) used when requesting background
+ * access. Also used when requesting both foreground and background access.
+ *
+ * From the "backgroundRequest" attribute or, if not set, {@link
+ * android.content.res.ResourceId#ID_NULL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int backgroundRequestResourceId;
+
+ /**
+ * A string resource identifier (in the package's resources) used as subtitle when requesting
+ * background access.
+ *
+ * From the "backgroundRequestDetail" attribute or, if not set, {@link
+ * android.content.res.ResourceId#ID_NULL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int backgroundRequestDetailResourceId;
+
+ /**
* The description string provided in the AndroidManifest file, if any. You
* probably don't want to use this, since it will be null if the description
* is in a resource. You probably want
@@ -76,6 +112,9 @@
super(orig);
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
+ requestDetailResourceId = orig.requestDetailResourceId;
+ backgroundRequestResourceId = orig.backgroundRequestResourceId;
+ backgroundRequestDetailResourceId = orig.backgroundRequestDetailResourceId;
nonLocalizedDescription = orig.nonLocalizedDescription;
flags = orig.flags;
priority = orig.priority;
@@ -119,6 +158,9 @@
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
+ dest.writeInt(requestDetailResourceId);
+ dest.writeInt(backgroundRequestResourceId);
+ dest.writeInt(backgroundRequestDetailResourceId);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
dest.writeInt(flags);
dest.writeInt(priority);
@@ -138,6 +180,9 @@
super(source);
descriptionRes = source.readInt();
requestRes = source.readInt();
+ requestDetailResourceId = source.readInt();
+ backgroundRequestResourceId = source.readInt();
+ backgroundRequestDetailResourceId = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
flags = source.readInt();
priority = source.readInt();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 938409a..535ef00 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -285,6 +285,21 @@
public int requestRes;
/**
+ * Some permissions only grant access while the app is in foreground. Some of these permissions
+ * allow to add background capabilities by adding another permission.
+ *
+ * If this is such a permission, this is the name of the permission adding the background
+ * access.
+ *
+ * From the "backgroundPermission" attribute or, if not set null
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public String backgroundPermission;
+
+ /**
* The description string provided in the AndroidManifest file, if any. You
* probably don't want to use this, since it will be null if the description
* is in a resource. You probably want
@@ -373,6 +388,7 @@
protectionLevel = orig.protectionLevel;
flags = orig.flags;
group = orig.group;
+ backgroundPermission = orig.backgroundPermission;
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
@@ -436,6 +452,7 @@
dest.writeInt(protectionLevel);
dest.writeInt(flags);
dest.writeString(group);
+ dest.writeString(backgroundPermission);
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
@@ -475,6 +492,7 @@
protectionLevel = source.readInt();
flags = source.readInt();
group = source.readString();
+ backgroundPermission = source.readString();
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
similarity index 65%
rename from packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
rename to core/java/android/hardware/biometrics/BiometricSourceType.aidl
index 19e556a..15440d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,9 @@
* limitations under the License.
*/
-package com.android.settingslib.suggestions;
+package android.hardware.biometrics;
-public class SuggestionCategory {
- public String category;
- public String pkg;
- public boolean multiple;
- public boolean exclusive;
- public long exclusiveExpireDaysInMillis;
-}
+/**
+ * @hide
+ */
+parcelable BiometricSourceType;
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricSourceType.java b/core/java/android/hardware/biometrics/BiometricSourceType.java
new file mode 100644
index 0000000..4a08cf2
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public enum BiometricSourceType implements Parcelable {
+ FINGERPRINT,
+ FACE,
+ IRIS;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(name());
+ }
+
+ public static final Creator<BiometricSourceType> CREATOR = new Creator<BiometricSourceType>() {
+ @Override
+ public BiometricSourceType createFromParcel(final Parcel source) {
+ return BiometricSourceType.valueOf(source.readString());
+ }
+
+ @Override
+ public BiometricSourceType[] newArray(final int size) {
+ return new BiometricSourceType[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 87c64cd..32c6898 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2365,13 +2365,25 @@
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, is defined relative to the active array rectangle given in
* this field, with <code>(0, 0)</code> being the top-left of this rectangle.</p>
* <p>The active array may be smaller than the full pixel array, since the full array may
- * include black calibration pixels or other inactive regions, and geometric correction
- * resulting in scaling or cropping may have been applied.</p>
+ * include black calibration pixels or other inactive regions.</p>
+ * <p>For devices that do not support {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the active
+ * array must be the same as {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</p>
+ * <p>For devices that support {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the active array must
+ * be enclosed by {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. The difference between
+ * pre-correction active array and active array accounts for scaling or cropping caused
+ * by lens geometric distortion correction.</p>
+ * <p>In general, application should always refer to active array size for controls like
+ * metering regions or crop region. Two exceptions are when the application is dealing with
+ * RAW image buffers (RAW_SENSOR, RAW10, RAW12 etc), or when application explicitly set
+ * {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} to OFF. In these cases, application should refer
+ * to {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</p>
* <p><b>Units</b>: Pixel coordinates on the image sensor</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE =
@@ -2616,9 +2628,9 @@
* <ol>
* <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
* </ol>
- * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
- * as the post-distortion-corrected rectangle given in
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
+ * <p>If the camera device doesn't support geometric distortion correction, or all of the
+ * geometric distortion fields are no-ops, this rectangle will be the same as the
+ * post-distortion-corrected rectangle given in {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p>This rectangle is defined relative to the full pixel array; (0,0) is the top-left of
* the full pixel array, and the size of the full pixel array is given by
* {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</p>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 411a97e..aca77a5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1269,11 +1269,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AE android.control.maxRegionsAe}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1289,15 +1304,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AE
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
@@ -1443,11 +1463,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of focus areas supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AF android.control.maxRegionsAf}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1464,15 +1499,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AF
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
@@ -1612,11 +1652,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AWB android.control.maxRegionsAwb}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must range from 0 to 1000, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1632,15 +1687,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AWB
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
@@ -2433,9 +2493,17 @@
/**
* <p>The desired region of the sensor to read out for this capture.</p>
* <p>This control can be used to implement digital zoom.</p>
- * <p>The crop region coordinate system is based off
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
- * top-left corner of the sensor active array.</p>
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Output streams use this rectangle to produce their output,
* cropping to a smaller region if necessary to maintain the
* stream's aspect ratio, then scaling the sensor input to
@@ -2454,20 +2522,30 @@
* outputs will crop horizontally (pillarbox), and 16:9
* streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The width and height of the crop region cannot
- * be set to be smaller than
+ * <p>If the coordinate system is android.sensor.info.activeArraysSize, the width and height
+ * of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
* <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+ * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, the width
+ * and height of the crop region cannot be set to be smaller than
+ * <code>floor( preCorrectionActiveArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>
+ * and
+ * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
+ * respectively.</p>
* <p>The camera device may adjust the crop region to account
* for rounding and other hardware requirements; the final
* crop region used will be included in the output capture
* result.</p>
* <p><b>Units</b>: Pixel coordinates relative to
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
+ * capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
@@ -3186,15 +3264,14 @@
* any correction at all would slow down capture rate. Every output stream will have a
* similar amount of enhancement applied.</p>
* <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
- * applied to any RAW output. Metadata coordinates such as face rectangles or metering
+ * applied to any RAW output. Metadata coordinates such as face rectangles or metering
* regions are also not affected by correction.</p>
- * <p>Applications enabling distortion correction need to pay extra attention when converting
- * image coordinates between corrected output buffers and the sensor array. For example, if
- * the app supports tap-to-focus and enables correction, it then has to apply the distortion
- * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
- * calculate the tap position on the sensor active array to be used with
- * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
- * they need to be drawn on top of the corrected output buffers.</p>
+ * <p>This control will be on by default on devices that support this control. Applications
+ * disabling distortion correction need to pay extra attention with the coordinate system of
+ * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+ * metadata coordinates follow the coordinate system of
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. When distortion is not OFF, metadata
+ * coordinates follow the coordinate system of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
@@ -3205,9 +3282,10 @@
* {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
- * @see CaptureRequest#CONTROL_AF_REGIONS
* @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
* @see CameraCharacteristics#LENS_DISTORTION
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see #DISTORTION_CORRECTION_MODE_OFF
* @see #DISTORTION_CORRECTION_MODE_FAST
* @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 361d83d..d003f9a 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -730,11 +730,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AE android.control.maxRegionsAe}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -750,15 +765,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AE
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
@@ -1152,11 +1172,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of focus areas supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AF android.control.maxRegionsAf}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1173,15 +1208,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AF
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
@@ -1730,11 +1770,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AWB android.control.maxRegionsAwb}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must range from 0 to 1000, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1750,15 +1805,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AWB
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
@@ -3099,9 +3159,17 @@
/**
* <p>The desired region of the sensor to read out for this capture.</p>
* <p>This control can be used to implement digital zoom.</p>
- * <p>The crop region coordinate system is based off
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
- * top-left corner of the sensor active array.</p>
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Output streams use this rectangle to produce their output,
* cropping to a smaller region if necessary to maintain the
* stream's aspect ratio, then scaling the sensor input to
@@ -3120,20 +3188,30 @@
* outputs will crop horizontally (pillarbox), and 16:9
* streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The width and height of the crop region cannot
- * be set to be smaller than
+ * <p>If the coordinate system is android.sensor.info.activeArraysSize, the width and height
+ * of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
* <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+ * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, the width
+ * and height of the crop region cannot be set to be smaller than
+ * <code>floor( preCorrectionActiveArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>
+ * and
+ * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
+ * respectively.</p>
* <p>The camera device may adjust the crop region to account
* for rounding and other hardware requirements; the final
* crop region used will be included in the output capture
* result.</p>
* <p><b>Units</b>: Pixel coordinates relative to
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
+ * capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
@@ -3624,12 +3702,23 @@
/**
* <p>List of landmarks for detected
* faces.</p>
- * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL
* This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
@@ -3639,12 +3728,23 @@
/**
* <p>List of the bounding rectangles for detected
* faces.</p>
- * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF
* This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
@@ -4478,15 +4578,14 @@
* any correction at all would slow down capture rate. Every output stream will have a
* similar amount of enhancement applied.</p>
* <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
- * applied to any RAW output. Metadata coordinates such as face rectangles or metering
+ * applied to any RAW output. Metadata coordinates such as face rectangles or metering
* regions are also not affected by correction.</p>
- * <p>Applications enabling distortion correction need to pay extra attention when converting
- * image coordinates between corrected output buffers and the sensor array. For example, if
- * the app supports tap-to-focus and enables correction, it then has to apply the distortion
- * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
- * calculate the tap position on the sensor active array to be used with
- * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
- * they need to be drawn on top of the corrected output buffers.</p>
+ * <p>This control will be on by default on devices that support this control. Applications
+ * disabling distortion correction need to pay extra attention with the coordinate system of
+ * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+ * metadata coordinates follow the coordinate system of
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. When distortion is not OFF, metadata
+ * coordinates follow the coordinate system of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
@@ -4497,9 +4596,10 @@
* {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
- * @see CaptureRequest#CONTROL_AF_REGIONS
* @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
* @see CameraCharacteristics#LENS_DISTORTION
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see #DISTORTION_CORRECTION_MODE_OFF
* @see #DISTORTION_CORRECTION_MODE_FAST
* @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a040a09..1ee3c93 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,9 +82,9 @@
*
* </ul>
*
- * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
- * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
- * format may be used.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
+ * {@link ImageFormat#JPEG} and {@link ImageFormat#RAW_PRIVATE} can be used for sharing, subject to
+ * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
*
* @see CameraDevice#createCaptureSessionByOutputConfigurations
*
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index c56b685..414c463 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -725,7 +725,7 @@
* Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
* for the class/size combination (in nanoseconds).
*
- * <p>This assumes a the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
+ * <p>This assumes that the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
* For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
*
* <p>{@code klass} should be one of the ones which is supported by
@@ -870,7 +870,7 @@
/**
* Get the stall duration for the class/size combination (in nanoseconds).
*
- * <p>This assumes a the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
+ * <p>This assumes that the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
* For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
*
* <p>{@code klass} should be one of the ones with a non-empty array returned by
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java b/core/java/android/hardware/face/Face.aidl
similarity index 65%
copy from packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
copy to core/java/android/hardware/face/Face.aidl
index 19e556a..a7c9141 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
+++ b/core/java/android/hardware/face/Face.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.face;
-package com.android.settingslib.suggestions;
-
-public class SuggestionCategory {
- public String category;
- public String pkg;
- public boolean multiple;
- public boolean exclusive;
- public long exclusiveExpireDaysInMillis;
-}
+/**
+ * @hide
+ */
+parcelable Face;
diff --git a/core/java/android/hardware/face/Face.java b/core/java/android/hardware/face/Face.java
new file mode 100644
index 0000000..c07351d
--- /dev/null
+++ b/core/java/android/hardware/face/Face.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for face metadata.
+ *
+ * @hide
+ */
+public final class Face extends BiometricAuthenticator.BiometricIdentifier {
+ private CharSequence mName;
+ private int mFaceId;
+ private long mDeviceId; // physical device this face is associated with
+
+ public Face(CharSequence name, int faceId, long deviceId) {
+ mName = name;
+ mFaceId = faceId;
+ mDeviceId = deviceId;
+ }
+
+ private Face(Parcel in) {
+ mName = in.readString();
+ mFaceId = in.readInt();
+ mDeviceId = in.readLong();
+ }
+
+ /**
+ * Gets the human-readable name for the given fingerprint.
+ * @return name given to finger
+ */
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /**
+ * Gets the device-specific finger id. Used by Settings to map a name to a specific
+ * fingerprint template.
+ * @return device-specific id for this finger
+ * @hide
+ */
+ public int getFaceId() {
+ return mFaceId;
+ }
+
+ /**
+ * Device this face belongs to.
+ *
+ * @hide
+ */
+ public long getDeviceId() {
+ return mDeviceId;
+ }
+
+ /**
+ * Describes the contents.
+ * @return
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes to a parcel.
+ * @param out
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mName.toString());
+ out.writeInt(mFaceId);
+ out.writeLong(mDeviceId);
+ }
+
+ public static final Parcelable.Creator<Face> CREATOR = new Parcelable.Creator<Face>() {
+ public Face createFromParcel(Parcel in) {
+ return new Face(in);
+ }
+
+ public Face[] newArray(int size) {
+ return new Face[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
new file mode 100644
index 0000000..92f1990
--- /dev/null
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -0,0 +1,1149 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A class that coordinates access to the face authentication hardware.
+ * @hide
+ */
+@SystemService(Context.FACE_SERVICE)
+public class FaceManager {
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int FACE_ERROR_HW_UNAVAILABLE = 1;
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ public static final int FACE_ERROR_UNABLE_TO_PROCESS = 2;
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the face authentication sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ public static final int FACE_ERROR_TIMEOUT = 3;
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ public static final int FACE_ERROR_NO_SPACE = 4;
+ /**
+ * The operation was canceled because the face authentication sensor is unavailable. For
+ * example, this may happen when the user is switched, the device is locked or another pending
+ * operation prevents or disables it.
+ */
+ public static final int FACE_ERROR_CANCELED = 5;
+ /**
+ * The {@link FaceManager#remove} call failed. Typically this will happen when the
+ * provided face id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FACE_ERROR_UNABLE_TO_REMOVE = 6;
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ public static final int FACE_ERROR_LOCKOUT = 7;
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not covered by the ones in HAL h file. Applications are
+ * expected to show the error message string if they happen, but are advised not to rely on the
+ * message id since they will be device and vendor-specific
+ */
+ public static final int FACE_ERROR_VENDOR = 8;
+ //
+ // Error messages from face authentication hardware during initialization, enrollment,
+ // authentication or removal. Must agree with the list in HAL h file
+ //
+ /**
+ * The operation was canceled because FACE_ERROR_LOCKOUT occurred too many times.
+ * Face authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ public static final int FACE_ERROR_LOCKOUT_PERMANENT = 9;
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return
+ * to face authentication, such as a "use face authentication" button.
+ */
+ public static final int FACE_ERROR_USER_CANCELED = 10;
+ /**
+ * The user does not have a face enrolled.
+ */
+ public static final int FACE_ERROR_NOT_ENROLLED = 11;
+ /**
+ * The device does not have a face sensor. This message will propagate if the calling app
+ * ignores the result from PackageManager.hasFeature(FEATURE_FACE) and calls
+ * this API anyway. Apps should always check for the feature before calling this API.
+ */
+ public static final int FACE_ERROR_HW_NOT_PRESENT = 12;
+ /**
+ * @hide
+ */
+ public static final int FACE_ERROR_VENDOR_BASE = 1000;
+ /**
+ * The image acquired was good.
+ */
+ public static final int FACE_ACQUIRED_GOOD = 0;
+ /**
+ * The face image was not good enough to process due to a detected condition.
+ * (See {@link #FACE_ACQUIRED_TOO_BRIGHT or @link #FACE_ACQUIRED_TOO_DARK}).
+ */
+ public static final int FACE_ACQUIRED_INSUFFICIENT = 1;
+ /**
+ * The face image was too bright due to too much ambient light.
+ * For example, it's reasonable to return this after multiple
+ * {@link #FACE_ACQUIRED_INSUFFICIENT}
+ * The user is expected to take action to retry in better lighting conditions
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_BRIGHT = 2;
+ /**
+ * The face image was too dark due to illumination light obscured.
+ * For example, it's reasonable to return this after multiple
+ * {@link #FACE_ACQUIRED_INSUFFICIENT}
+ * The user is expected to take action to retry in better lighting conditions
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_DARK = 3;
+ /**
+ * The detected face is too close to the sensor, and the image can't be processed.
+ * The user should be informed to move farther from the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_CLOSE = 4;
+ /**
+ * The detected face is too small, as the user might be too far from the sensor.
+ * The user should be informed to move closer to the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_FAR = 5;
+ /**
+ * Only the upper part of the face was detected. The sensor field of view is too high.
+ * The user should be informed to move up with respect to the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_HIGH = 6;
+ /**
+ * Only the lower part of the face was detected. The sensor field of view is too low.
+ * The user should be informed to move down with respect to the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_LOW = 7;
+
+ //
+ // Image acquisition messages. Must agree with those in HAL h file
+ //
+ /**
+ * Only the right part of the face was detected. The sensor field of view is too far right.
+ * The user should be informed to move to the right with respect to the sensor
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_RIGHT = 8;
+ /**
+ * Only the left part of the face was detected. The sensor field of view is too far left.
+ * The user should be informed to move to the left with respect to the sensor
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_LEFT = 9;
+ /**
+ * User's gaze strayed too far from the sensor causing significant parts of the user's face
+ * to be hidden.
+ * The user should be informed to turn the face front to the sensor.
+ */
+ public static final int FACE_ACQUIRED_POOR_GAZE = 10;
+ /**
+ * No face was detected in front of the sensor.
+ * The user should be informed to point the sensor to a face when this is returned.
+ */
+ public static final int FACE_ACQUIRED_NOT_DETECTED = 11;
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ *
+ * @hide
+ */
+ public static final int FACE_ACQUIRED_VENDOR = 12;
+ /**
+ * @hide
+ */
+ public static final int FACE_ACQUIRED_VENDOR_BASE = 1000;
+ private static final String TAG = "FaceManager";
+ private static final boolean DEBUG = true;
+ private static final int MSG_ENROLL_RESULT = 100;
+ private static final int MSG_ACQUIRED = 101;
+ private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
+ private static final int MSG_AUTHENTICATION_FAILED = 103;
+ private static final int MSG_ERROR = 104;
+ private static final int MSG_REMOVED = 105;
+ private final Context mContext;
+ private IFaceService mService;
+ private IBinder mToken = new Binder();
+ private AuthenticationCallback mAuthenticationCallback;
+ private EnrollmentCallback mEnrollmentCallback;
+ private RemovalCallback mRemovalCallback;
+ private CryptoObject mCryptoObject;
+ private Face mRemovalFace;
+ private Handler mHandler;
+ private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
+
+ @Override // binder call
+ public void onEnrollResult(long deviceId, int faceId, int remaining) {
+ mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
+ new Face(null, faceId, deviceId)).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onAuthenticationSucceeded(long deviceId, Face face) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, face).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onAuthenticationFailed(long deviceId) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onError(long deviceId, int error, int vendorCode) {
+ mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onRemoved(long deviceId, int faceId, int remaining) {
+ mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
+ new Face(null, faceId, deviceId)).sendToTarget();
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public FaceManager(Context context, IFaceService service) {
+ mContext = context;
+ mService = service;
+ if (mService == null) {
+ Slog.v(TAG, "FaceAuthenticationManagerService was null");
+ }
+ mHandler = new MyHandler(context);
+ }
+
+ /**
+ * Request authentication of a crypto object. This call operates the face recognition hardware
+ * and starts capturing images. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ *
+ * @param crypto object associated with the call or null if none required.
+ * @param cancel an object that can be used to cancel authentication
+ * @param flags optional flags; should be 0
+ * @param callback an object to receive authentication events
+ * @param handler an optional handler to handle callback events
+ * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
+ * by
+ * <a href="{@docRoot}training/articles/keystore.html">Android
+ * Keystore facility</a>.
+ * @throws IllegalStateException if the crypto primitive is not initialized.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+ int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+ authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
+ }
+
+ /**
+ * Use the provided handler thread for events.
+ */
+ private void useHandler(Handler handler) {
+ if (handler != null) {
+ mHandler = new MyHandler(handler.getLooper());
+ } else if (mHandler.getLooper() != mContext.getMainLooper()) {
+ mHandler = new MyHandler(mContext.getMainLooper());
+ }
+ }
+
+ /**
+ * Per-user version
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+ int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply an authentication callback");
+ }
+
+ if (cancel != null) {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "authentication already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+ }
+ }
+
+ if (mService != null) {
+ try {
+ useHandler(handler);
+ mAuthenticationCallback = callback;
+ mCryptoObject = crypto;
+ long sessionId = crypto != null ? crypto.getOpId() : 0;
+ mService.authenticate(mToken, sessionId, mServiceReceiver, flags,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception while authenticating: ", e);
+ if (callback != null) {
+ // Though this may not be a hardware issue, it will cause apps to give up or try
+ // again later.
+ callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ }
+ }
+ }
+ }
+
+ /**
+ * Request face authentication enrollment. This call operates the face authentication hardware
+ * and starts capturing images. Progress will be indicated by callbacks to the
+ * {@link EnrollmentCallback} object. It terminates when
+ * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+ * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ *
+ * @param token a unique token provided by a recent creation or verification of device
+ * credentials (e.g. pin, pattern or password).
+ * @param cancel an object that can be used to cancel enrollment
+ * @param flags optional flags
+ * @param userId the user to whom this face will belong to
+ * @param callback an object to receive enrollment events
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public void enroll(byte[] token, CancellationSignal cancel, int flags,
+ int userId, EnrollmentCallback callback) {
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = getCurrentUserId();
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply an enrollment callback");
+ }
+
+ if (cancel != null) {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "enrollment already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnEnrollCancelListener());
+ }
+ }
+
+ if (mService != null) {
+ try {
+ mEnrollmentCallback = callback;
+ mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception in enroll: ", e);
+ if (callback != null) {
+ // Though this may not be a hardware issue, it will cause apps to give up or try
+ // again later.
+ callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ }
+ }
+ }
+ }
+
+ /**
+ * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
+ * existing device credentials (e.g. pin/pattern/password).
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public long preEnroll() {
+ long result = 0;
+ if (mService != null) {
+ try {
+ result = mService.preEnroll(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Finishes enrollment and cancels the current auth token.
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public int postEnroll() {
+ int result = 0;
+ if (mService != null) {
+ try {
+ result = mService.postEnroll(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Sets the active user. This is meant to be used to select the current profile for enrollment
+ * to allow separate enrolled faces for a work profile
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public void setActiveUser(int userId) {
+ if (mService != null) {
+ try {
+ mService.setActiveUser(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Remove given face template from face hardware and/or protected storage.
+ *
+ * @param face the face item to remove
+ * @param userId the user who this face belongs to
+ * @param callback an optional callback to verify that face templates have been
+ * successfully removed. May be null if no callback is required.
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public void remove(Face face, int userId, RemovalCallback callback) {
+ if (mService != null) {
+ try {
+ mRemovalCallback = callback;
+ mRemovalFace = face;
+ mService.remove(mToken, userId, mServiceReceiver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception in remove: ", e);
+ if (callback != null) {
+ callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ }
+ }
+ }
+ }
+
+ /**
+ * Obtain the enrolled face template.
+ *
+ * @return the current face item
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public Face getEnrolledFace(int userId) {
+ if (mService != null) {
+ try {
+ return mService.getEnrolledFace(userId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Obtain the enrolled face template.
+ *
+ * @return the current face item
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public Face getEnrolledFace() {
+ return getEnrolledFace(UserHandle.myUserId());
+ }
+
+ /**
+ * Determine if there is a face enrolled.
+ *
+ * @return true if a face is enrolled, false otherwise
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public boolean hasEnrolledFace() {
+ if (mService != null) {
+ try {
+ return mService.hasEnrolledFace(
+ UserHandle.myUserId(), mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ USE_BIOMETRIC,
+ INTERACT_ACROSS_USERS})
+ public boolean hasEnrolledFace(int userId) {
+ if (mService != null) {
+ try {
+ return mService.hasEnrolledFace(userId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine if face authentication sensor hardware is present and functional.
+ *
+ * @return true if hardware is present and functional, false otherwise.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public boolean isHardwareDetected() {
+ if (mService != null) {
+ try {
+ long deviceId = 0; /* TODO: plumb hardware id to FPMS */
+ return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves the authenticator token for binding keys to the lifecycle
+ * of the calling user's face. Used only by internal clients.
+ *
+ * @hide
+ */
+ public long getAuthenticatorId() {
+ if (mService != null) {
+ try {
+ return mService.getAuthenticatorId(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+ }
+ return 0;
+ }
+
+ /**
+ * Reset the lockout timer when asked to do so by keyguard.
+ *
+ * @param token an opaque token returned by password confirmation.
+ * @hide
+ */
+ public void resetTimeout(byte[] token) {
+ if (mService != null) {
+ try {
+ mService.resetTimeout(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "resetTimeout(): Service not connected!");
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void addLockoutResetCallback(final LockoutResetCallback callback) {
+ if (mService != null) {
+ try {
+ final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ mService.addLockoutResetCallback(
+ new IFaceServiceLockoutResetCallback.Stub() {
+
+ @Override
+ public void onLockoutReset(long deviceId,
+ IRemoteCallback serverCallback)
+ throws RemoteException {
+ try {
+ final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "faceLockoutResetCallback");
+ wakeLock.acquire();
+ mHandler.post(() -> {
+ try {
+ callback.onLockoutReset();
+ } finally {
+ wakeLock.release();
+ }
+ });
+ } finally {
+ serverCallback.sendResult(null /* data */);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
+ }
+ }
+
+ private int getCurrentUserId() {
+ try {
+ return ActivityManager.getService().getCurrentUser().id;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void cancelEnrollment() {
+ if (mService != null) {
+ try {
+ mService.cancelEnrollment(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void cancelAuthentication(CryptoObject cryptoObject) {
+ if (mService != null) {
+ try {
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private String getErrorString(int errMsg, int vendorCode) {
+ switch (errMsg) {
+ case FACE_ERROR_HW_UNAVAILABLE:
+ return mContext.getString(
+ com.android.internal.R.string.face_error_hw_not_available);
+ case FACE_ERROR_UNABLE_TO_PROCESS:
+ return mContext.getString(
+ com.android.internal.R.string.face_error_unable_to_process);
+ case FACE_ERROR_TIMEOUT:
+ return mContext.getString(com.android.internal.R.string.face_error_timeout);
+ case FACE_ERROR_NO_SPACE:
+ return mContext.getString(com.android.internal.R.string.face_error_no_space);
+ case FACE_ERROR_CANCELED:
+ return mContext.getString(com.android.internal.R.string.face_error_canceled);
+ case FACE_ERROR_LOCKOUT:
+ return mContext.getString(com.android.internal.R.string.face_error_lockout);
+ case FACE_ERROR_LOCKOUT_PERMANENT:
+ return mContext.getString(
+ com.android.internal.R.string.face_error_lockout_permanent);
+ case FACE_ERROR_NOT_ENROLLED:
+ return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+ case FACE_ERROR_HW_NOT_PRESENT:
+ return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+ case FACE_ERROR_VENDOR: {
+ String[] msgArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.face_error_vendor);
+ if (vendorCode < msgArray.length) {
+ return msgArray[vendorCode];
+ }
+ }
+ }
+ Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
+ return null;
+ }
+
+ private String getAcquiredString(int acquireInfo, int vendorCode) {
+ switch (acquireInfo) {
+ case FACE_ACQUIRED_GOOD:
+ return null;
+ case FACE_ACQUIRED_INSUFFICIENT:
+ return mContext.getString(R.string.face_acquired_insufficient);
+ case FACE_ACQUIRED_TOO_BRIGHT:
+ return mContext.getString(R.string.face_acquired_too_bright);
+ case FACE_ACQUIRED_TOO_DARK:
+ return mContext.getString(R.string.face_acquired_too_dark);
+ case FACE_ACQUIRED_TOO_CLOSE:
+ return mContext.getString(R.string.face_acquired_too_close);
+ case FACE_ACQUIRED_TOO_FAR:
+ return mContext.getString(R.string.face_acquired_too_far);
+ case FACE_ACQUIRED_TOO_HIGH:
+ return mContext.getString(R.string.face_acquired_too_high);
+ case FACE_ACQUIRED_TOO_LOW:
+ return mContext.getString(R.string.face_acquired_too_low);
+ case FACE_ACQUIRED_TOO_RIGHT:
+ return mContext.getString(R.string.face_acquired_too_right);
+ case FACE_ACQUIRED_TOO_LEFT:
+ return mContext.getString(R.string.face_acquired_too_left);
+ case FACE_ACQUIRED_POOR_GAZE:
+ return mContext.getString(R.string.face_acquired_poor_gaze);
+ case FACE_ACQUIRED_NOT_DETECTED:
+ return mContext.getString(R.string.face_acquired_not_detected);
+ case FACE_ACQUIRED_VENDOR: {
+ String[] msgArray = mContext.getResources().getStringArray(
+ R.array.face_acquired_vendor);
+ if (vendorCode < msgArray.length) {
+ return msgArray[vendorCode];
+ }
+ }
+ }
+ Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
+ return null;
+ }
+
+ /**
+ * A wrapper class for the crypto objects supported by FaceAuthenticationManager.
+ */
+ public static final class CryptoObject {
+
+ private final Object mCrypto;
+
+ public CryptoObject(@NonNull Signature signature) {
+ mCrypto = signature;
+ }
+
+ public CryptoObject(@NonNull Cipher cipher) {
+ mCrypto = cipher;
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ mCrypto = mac;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ *
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() {
+ return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Cipher} object.
+ *
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() {
+ return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+ }
+
+ /**
+ * Get {@link Mac} object.
+ *
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() {
+ return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+ }
+
+ /**
+ * @return the opId associated with this object or 0 if none
+ * @hide
+ */
+ public long getOpId() {
+ return mCrypto != null
+ ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ }
+ }
+
+ /**
+ * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}.
+ */
+ public static class AuthenticationResult {
+ private Face mFace;
+ private CryptoObject mCryptoObject;
+ private int mUserId;
+
+ /**
+ * Authentication result
+ *
+ * @param crypto the crypto object
+ * @param face the recognized face data, if allowed.
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
+ mCryptoObject = crypto;
+ mFace = face;
+ mUserId = userId;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ *
+ * @return crypto object provided to {@link FaceManager#authenticate
+ * (CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}.
+ */
+ public CryptoObject getCryptoObject() {
+ return mCryptoObject;
+ }
+
+ /**
+ * Obtain the Face associated with this operation. Applications are strongly
+ * discouraged from associating specific faces with specific applications or operations.
+ *
+ * @hide
+ */
+ public Face getFace() {
+ return mFace;
+ }
+
+ /**
+ * Obtain the userId for which this face was authenticated.
+ *
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
+ * FaceManager#authenticate(CryptoObject, CancellationSignal,
+ * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
+ * to face events.
+ */
+ public abstract static class AuthenticationCallback {
+
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ *
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
+ */
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ }
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Sensor dirty, please clean it."
+ *
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
+ */
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
+ }
+
+ /**
+ * Called when a face is recognized.
+ *
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {
+ }
+
+ /**
+ * Called when a face is detected but not recognized.
+ */
+ public void onAuthenticationFailed() {
+ }
+
+ /**
+ * Called when a face image has been acquired, but wasn't processed yet.
+ *
+ * @param acquireInfo one of FACE_ACQUIRED_* constants
+ * @hide
+ */
+ public void onAuthenticationAcquired(int acquireInfo) {
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FaceManager#enroll(long,
+ * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
+ * must provide an implementation of this to {@link FaceManager#enroll(long,
+ * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
+ *
+ * @hide
+ */
+ public abstract static class EnrollmentCallback {
+
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ *
+ * @param errMsgId An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
+ */
+ public void onEnrollmentError(int errMsgId, CharSequence errString) {
+ }
+
+ /**
+ * Called when a recoverable error has been encountered during enrollment. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Image too dark, uncover light source" or what they need to do next, such as
+ * "Rotate face up / down."
+ *
+ * @param helpMsgId An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
+ */
+ public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+ }
+
+ /**
+ * Called as each enrollment step progresses. Enrollment is considered complete when
+ * remaining reaches 0. This function will not be called if enrollment fails. See
+ * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
+ *
+ * @param remaining The number of remaining steps
+ * @param vendorMsg Vendor feedback about the current enroll attempt. Use it to customize
+ * the GUI according to vendor's requirements.
+ */
+ public void onEnrollmentProgress(int remaining, long vendorMsg) {
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
+ * may
+ * optionally provide an implementation of this to
+ * {@link #remove(Face, int, RemovalCallback)} for listening to face template
+ * removal events.
+ *
+ * @hide
+ */
+ public abstract static class RemovalCallback {
+
+ /**
+ * Called when the given face can't be removed.
+ *
+ * @param face The face that the call attempted to remove
+ * @param errMsgId An associated error message id
+ * @param errString An error message indicating why the face id can't be removed
+ */
+ public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
+ }
+
+ /**
+ * Called when a given face is successfully removed.
+ *
+ * @param face The face template that was removed.
+ */
+ public void onRemovalSucceeded(Face face) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public abstract static class LockoutResetCallback {
+
+ /**
+ * Called when lockout period expired and clients are allowed to listen for face
+ * authentication
+ * again.
+ */
+ public void onLockoutReset() {
+ }
+ }
+
+ private class OnEnrollCancelListener implements OnCancelListener {
+ @Override
+ public void onCancel() {
+ cancelEnrollment();
+ }
+ }
+
+ private class OnAuthenticationCancelListener implements OnCancelListener {
+ private CryptoObject mCrypto;
+
+ OnAuthenticationCancelListener(CryptoObject crypto) {
+ mCrypto = crypto;
+ }
+
+ @Override
+ public void onCancel() {
+ cancelAuthentication(mCrypto);
+ }
+ }
+
+ private class MyHandler extends Handler {
+ private MyHandler(Context context) {
+ super(context.getMainLooper());
+ }
+
+ private MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_ENROLL_RESULT:
+ sendEnrollResult((EnrollResultMsg) msg.obj);
+ break;
+ case MSG_ACQUIRED:
+ sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
+ msg.arg2 /* vendorCode */);
+ break;
+ case MSG_AUTHENTICATION_SUCCEEDED:
+ sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
+ break;
+ case MSG_AUTHENTICATION_FAILED:
+ sendAuthenticatedFailed();
+ break;
+ case MSG_ERROR:
+ sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
+ msg.arg2 /* vendorCode */);
+ break;
+ case MSG_REMOVED:
+ sendRemovedResult((Face) msg.obj);
+ break;
+ }
+ }
+
+ private void sendRemovedResult(Face face) {
+ if (mRemovalCallback == null) {
+ return;
+ }
+ if (face == null) {
+ Log.e(TAG, "Received MSG_REMOVED, but face is null");
+ return;
+ }
+
+
+ mRemovalCallback.onRemovalSucceeded(face);
+ }
+
+ private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
+ // emulate HAL 2.1 behavior and send real errMsgId
+ final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
+ ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mRemovalCallback != null) {
+ mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ }
+ }
+
+ private void sendEnrollResult(EnrollResultMsg faceWrapper) {
+ if (mEnrollmentCallback != null) {
+ int remaining = faceWrapper.getRemaining();
+ long vendorMsg = faceWrapper.getVendorMsg();
+ mEnrollmentCallback.onEnrollmentProgress(remaining, vendorMsg);
+ }
+ }
+
+ private void sendAuthenticatedSucceeded(Face face, int userId) {
+ if (mAuthenticationCallback != null) {
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, face, userId);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ }
+ }
+
+ private void sendAuthenticatedFailed() {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationFailed();
+ }
+ }
+
+ private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+ }
+ final String msg = getAcquiredString(acquireInfo, vendorCode);
+ if (msg == null) {
+ return;
+ }
+ final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
+ ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ }
+ }
+ }
+
+ private class EnrollResultMsg {
+ private final Face mFace;
+ private final int mRemaining;
+ private final long mVendorMsg;
+
+ EnrollResultMsg(Face face, int remaining, long vendorMsg) {
+ mFace = face;
+ mRemaining = remaining;
+ mVendorMsg = vendorMsg;
+ }
+
+ Face getFace() {
+ return mFace;
+ }
+
+ long getVendorMsg() {
+ return mVendorMsg;
+ }
+
+ int getRemaining() {
+ return mRemaining;
+ }
+ }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
new file mode 100644
index 0000000..856a313
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+import android.os.Bundle;
+import android.hardware.face.IFaceServiceReceiver;
+import android.hardware.face.IFaceServiceLockoutResetCallback;
+import android.hardware.face.Face;
+
+/**
+ * Communication channel from client to the face service.
+ * @hide
+ */
+interface IFaceService {
+ // Authenticate the given sessionId with a face
+ void authenticate(IBinder token, long sessionId,
+ IFaceServiceReceiver receiver, int flags, String opPackageName);
+
+ // Cancel authentication for the given sessionId
+ void cancelAuthentication(IBinder token, String opPackageName);
+
+ // Start face enrollment
+ void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
+ int flags, String opPackageName);
+
+ // Cancel enrollment in progress
+ void cancelEnrollment(IBinder token);
+
+ // Any errors resulting from this call will be returned to the listener
+ void remove(IBinder token, int userId, IFaceServiceReceiver receiver);
+
+ // Get the enrolled face for user.
+ Face getEnrolledFace(int userId, String opPackageName);
+
+ // Determine if HAL is loaded and ready
+ boolean isHardwareDetected(long deviceId, String opPackageName);
+
+ // Get a pre-enrollment authentication token
+ long preEnroll(IBinder token);
+
+ // Finish an enrollment sequence and invalidate the authentication token
+ int postEnroll(IBinder token);
+
+ // Determine if a user has enrolled a face
+ boolean hasEnrolledFace(int userId, String opPackageName);
+
+ // Gets the number of hardware devices
+ // int getHardwareDeviceCount();
+
+ // Gets the unique device id for hardware enumerated at i
+ // long getHardwareDevice(int i);
+
+ // Gets the authenticator ID for face
+ long getAuthenticatorId(String opPackageName);
+
+ // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetTimeout(in byte [] cryptoToken);
+
+ // Add a callback which gets notified when the face lockout period expired.
+ void addLockoutResetCallback(IFaceServiceLockoutResetCallback callback);
+
+ // Explicitly set the active user (for enrolling work profile)
+ void setActiveUser(int uid);
+}
diff --git a/core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl b/core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl
new file mode 100644
index 0000000..b62fde3
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceServiceLockoutResetCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+import android.hardware.face.Face;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+import android.os.UserHandle;
+
+/**
+ * Callback when lockout period expired and clients are allowed to authenticate again.
+ * @hide
+ */
+oneway interface IFaceServiceLockoutResetCallback {
+
+ /**
+ * A wakelock will be held until the reciever calls back into {@param callback}
+ */
+ void onLockoutReset(long deviceId, IRemoteCallback callback);
+}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
new file mode 100644
index 0000000..16fb690
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+import android.hardware.face.Face;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FaceService back to FaceAuthenticationManager.
+ * @hide
+ */
+oneway interface IFaceServiceReceiver {
+ void onEnrollResult(long deviceId, int faceId, int remaining);
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
+ void onAuthenticationSucceeded(long deviceId, in Face face);
+ void onAuthenticationFailed(long deviceId);
+ void onError(long deviceId, int error, int vendorCode);
+ void onRemoved(long deviceId, int faceId, int remaining);
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b4c8a5e..431c651 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -370,19 +370,6 @@
InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
- /**
- * A token to keep tracking the last IPC that triggered
- * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
- * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
- * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
- *
- * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
- * disentangle event flows for various purposes such as better window animation and providing
- * fine-grained debugging information.</p>
- */
- @Nullable
- private IBinder mStartInputToken;
-
int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
@@ -528,7 +515,7 @@
public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
- mStartInputToken = startInputToken;
+ mImm.reportStartInput(mToken, startInputToken);
// This needs to be dispatched to interface methods rather than doStartInput().
// Otherwise IME developers who have overridden those interface methods will lose
@@ -579,8 +566,8 @@
}
clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
- mImm.setImeWindowStatus(mToken, mStartInputToken,
- mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+ mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()),
+ mBackDisposition);
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_SHOWN
@@ -1059,8 +1046,8 @@
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
- mImm.setImeWindowStatus(mToken, mStartInputToken,
- IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+ mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
+ mBackDisposition);
}
}
@@ -1110,8 +1097,7 @@
return;
}
mBackDisposition = disposition;
- mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
- mBackDisposition);
+ mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
}
/**
@@ -1861,8 +1847,7 @@
final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
if (previousImeWindowStatus != nextImeWindowStatus) {
- mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
- mBackDisposition);
+ mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
}
if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
@@ -1887,7 +1872,7 @@
}
private void doHideWindow() {
- mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
+ mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
hideWindow();
}
@@ -2869,7 +2854,6 @@
p.println(" mInputStarted=" + mInputStarted
+ " mInputViewStarted=" + mInputViewStarted
+ " mCandidatesViewStarted=" + mCandidatesViewStarted);
- p.println(" mStartInputToken=" + mStartInputToken);
if (mInputEditorInfo != null) {
p.println(" mInputEditorInfo:");
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 1aeac5f..3ca7f77 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -144,7 +144,6 @@
/**
* Set this incident report privacy policy spec.
- * @hide
*/
public void setPrivacyPolicy(int dest) {
switch (dest) {
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
new file mode 100644
index 0000000..60eb5c3
--- /dev/null
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * Variant of {@link FileDescriptor} that allows its creator to specify regions
+ * that should be redacted (appearing as zeros to the reader).
+ *
+ * @hide
+ */
+public class RedactingFileDescriptor {
+ private static final String TAG = "RedactingFileDescriptor";
+ private static final boolean DEBUG = true;
+
+ private final long[] mRedactRanges;
+
+ private FileDescriptor mInner = null;
+ private ParcelFileDescriptor mOuter = null;
+
+ private RedactingFileDescriptor(Context context, File file, long[] redactRanges)
+ throws IOException {
+ mRedactRanges = checkRangesArgument(redactRanges);
+
+ try {
+ try {
+ mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0);
+ mOuter = context.getSystemService(StorageManager.class)
+ .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ } catch (IOException e) {
+ IoUtils.closeQuietly(mInner);
+ IoUtils.closeQuietly(mOuter);
+ throw e;
+ }
+ }
+
+ private static long[] checkRangesArgument(long[] ranges) {
+ if (ranges.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ for (int i = 0; i < ranges.length - 1; i += 2) {
+ if (ranges[i] > ranges[i + 1]) {
+ throw new IllegalArgumentException();
+ }
+ }
+ return ranges;
+ }
+
+ /**
+ * Open the given {@link File} and returns a {@link ParcelFileDescriptor}
+ * that offers a redacted, read-only view of the underlying data.
+ *
+ * @param file The underlying file to open.
+ * @param redactRanges List of file offsets that should be redacted, stored
+ * as {@code [start1, end1, start2, end2, ...]}. Start values are
+ * inclusive and end values are exclusive.
+ */
+ public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges)
+ throws IOException {
+ return new RedactingFileDescriptor(context, file, redactRanges).mOuter;
+ }
+
+ private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
+ @Override
+ public long onGetSize() throws ErrnoException {
+ return Os.fstat(mInner).st_size;
+ }
+
+ @Override
+ public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+ int n = 0;
+ while (n < size) {
+ try {
+ final int res = Os.pread(mInner, data, n, size - n, offset + n);
+ if (res == 0) {
+ break;
+ } else {
+ n += res;
+ }
+ } catch (InterruptedIOException e) {
+ n += e.bytesTransferred;
+ }
+ }
+
+ // Redact any relevant ranges before returning
+ final long[] ranges = mRedactRanges;
+ for (int i = 0; i < ranges.length; i += 2) {
+ final long start = Math.max(offset, ranges[i]);
+ final long end = Math.min(offset + size, ranges[i + 1]);
+ for (long j = start; j < end; j++) {
+ data[(int) (j - offset)] = 0;
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+ throw new ErrnoException(TAG, OsConstants.EBADF);
+ }
+
+ @Override
+ public void onFsync() throws ErrnoException {
+ Os.fsync(mInner);
+ }
+
+ @Override
+ public void onRelease() {
+ if (DEBUG) Slog.v(TAG, "onRelease()");
+ IoUtils.closeQuietly(mInner);
+ }
+ };
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e341363..e445d83 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7553,9 +7553,6 @@
*/
public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
- private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f);
-
/**
* Whether the assist gesture should silence alerts.
*
@@ -7585,8 +7582,6 @@
*/
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
- private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR;
-
/**
* Control whether Night display is currently activated.
* @hide
@@ -8042,8 +8037,6 @@
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
ASSIST_GESTURE_ENABLED,
- ASSIST_GESTURE_SENSITIVITY,
- ASSIST_GESTURE_SETUP_COMPLETE,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_WAKE_ENABLED,
VR_DISPLAY_MODE,
@@ -8182,8 +8175,6 @@
VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
- VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR);
- VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
@@ -9927,6 +9918,39 @@
public static final String WIFI_SCORE_PARAMS =
"wifi_score_params";
+ /**
+ * Setting to enable logging WifiIsUnusableEvent in metrics
+ * which gets triggered when wifi becomes unusable.
+ * Disabled by default, and setting it to 1 will enable it.
+ * @hide
+ */
+ public static final String WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED =
+ "wifi_is_unusable_event_metrics_enabled";
+
+ /**
+ * The minimum number of txBad the framework has to observe
+ * to trigger a wifi data stall.
+ * @hide
+ */
+ public static final String WIFI_DATA_STALL_MIN_TX_BAD =
+ "wifi_data_stall_min_tx_bad";
+
+ /**
+ * The minimum number of txSuccess the framework has to observe
+ * to trigger a wifi data stall when rxSuccess is 0.
+ * @hide
+ */
+ public static final String WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX =
+ "wifi_data_stall_min_tx_success_without_rx";
+
+ /**
+ * Setting to enable logging Wifi LinkSpeedCounts in metrics.
+ * Disabled by default, and setting it to 1 will enable it.
+ * @hide
+ */
+ public static final String WIFI_LINK_SPEED_METRICS_ENABLED =
+ "wifi_link_speed_metrics_enabled";
+
/**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
@@ -11078,6 +11102,7 @@
*
* <pre>
* enabled (boolean)
+ * disable_home (boolean)
* disable_tilt_to_wake (boolean)
* disable_touch_to_wake (boolean)
* </pre>
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ec045b1..6b7b89c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,6 +41,7 @@
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+ DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
}
/**
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 671532c..df4d5c4 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -180,13 +180,12 @@
///////////////////////////////////////////////////////////////////////////
/**
- * Draws the specified display list onto this canvas. The display list can only
- * be drawn if {@link android.view.RenderNode#isValid()} returns true.
+ * Draws the specified display list onto this canvas.
*
* @param renderNode The RenderNode to draw.
*/
public void drawRenderNode(RenderNode renderNode) {
- nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList());
+ nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode);
}
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 7c25fac..e10eeb0 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -139,7 +139,9 @@
RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
}
- // Do not access directly unless you are ThreadedRenderer
+ /** Not for general use; use only if you are ThreadedRenderer or DisplayListCanvas.
+ * @hide
+ */
final long mNativeRenderNode;
private final View mOwningView;
@@ -159,15 +161,6 @@
}
/**
- * Immediately destroys the RenderNode
- * Only suitable for testing/benchmarking where waiting for the GC/finalizer
- * is not feasible.
- */
- public void destroy() {
- // TODO: Removed temporarily
- }
-
- /**
* Creates a new RenderNode that can be used to record batches of
* drawing operations, and store / apply render properties when drawn.
*
@@ -219,6 +212,14 @@
}
/**
+ * Same as {@link #start(int, int)} but with the RenderNode's width & height
+ */
+ public DisplayListCanvas start() {
+ return DisplayListCanvas.obtain(this,
+ nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
+ }
+
+ /**
* Ends the recording for this display list. A display list cannot be
* replayed if recording is not finished. Calling this method marks
* the display list valid and {@link #isValid()} will return true.
@@ -251,13 +252,6 @@
return nIsValid(mNativeRenderNode);
}
- long getNativeDisplayList() {
- if (!isValid()) {
- throw new IllegalStateException("The display list is not valid.");
- }
- return mNativeRenderNode;
- }
-
///////////////////////////////////////////////////////////////////////////
// Matrix manipulation
///////////////////////////////////////////////////////////////////////////
@@ -463,7 +457,6 @@
* @see #setHasOverlappingRendering(boolean)
*/
public boolean hasOverlappingRendering() {
- //noinspection SimplifiableIfStatement
return nHasOverlappingRendering(mNativeRenderNode);
}
@@ -1009,4 +1002,8 @@
private static native float nGetPivotX(long renderNode);
@CriticalNative
private static native float nGetPivotY(long renderNode);
+ @CriticalNative
+ private static native int nGetWidth(long renderNode);
+ @CriticalNative
+ private static native int nGetHeight(long renderNode);
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index aa1e407..2f975b6 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -850,7 +850,9 @@
void buildLayer(RenderNode node) {
- nBuildLayer(mNativeProxy, node.getNativeDisplayList());
+ if (node.isValid()) {
+ nBuildLayer(mNativeProxy, node.mNativeRenderNode);
+ }
}
@@ -928,7 +930,7 @@
* not the RenderNode from a View.
**/
public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) {
- return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height);
+ return nCreateHardwareBitmap(node.mNativeRenderNode, width, height);
}
/**
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 276f50a..e4c595b 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -531,7 +531,6 @@
throws IOException {
RenderNode node = RenderNode.create("ViewDebug", null);
profileViewAndChildren(view, node, out, true);
- node.destroy();
}
private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7944319..d0539ae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4652,7 +4652,7 @@
* which is added in order to fade it out in its old location should be removed
* once the animation is complete.</p>
*
- * @param view The view to be added
+ * @param view The view to be added. The view must not have a parent.
* @param index The index at which this view should be drawn, must be >= 0.
* This value is relative to the {@link #getChildAt(int) index} values in the normal
* child list of this container, where any transient view at a particular index will
@@ -4661,9 +4661,14 @@
* @hide
*/
public void addTransientView(View view, int index) {
- if (index < 0) {
+ if (index < 0 || view == null) {
return;
}
+ if (view.mParent != null) {
+ throw new IllegalStateException("The specified view already has a parent "
+ + view.mParent);
+ }
+
if (mTransientIndices == null) {
mTransientIndices = new ArrayList<Integer>();
mTransientViews = new ArrayList<View>();
@@ -4683,7 +4688,9 @@
mTransientViews.add(view);
}
view.mParent = this;
- view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
+ if (mAttachInfo != null) {
+ view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags & VISIBILITY_MASK));
+ }
invalidate(true);
}
@@ -4705,7 +4712,9 @@
mTransientViews.remove(i);
mTransientIndices.remove(i);
view.mParent = null;
- view.dispatchDetachedFromWindow();
+ if (view.mAttachInfo != null) {
+ view.dispatchDetachedFromWindow();
+ }
invalidate(true);
return;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e989e1a..8f28102 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1435,7 +1435,9 @@
// set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
// service set the extra and returned RESULT_CANCELED...
- if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
+ if (sDebug) {
+ Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
+ }
synchronized (mLock) {
if (!isActiveLocked()) {
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 738ff4d..6b26f23 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -34,6 +34,9 @@
*
* {@hide}
*/
+ // TODO(b/73536867) STOPSHIP : this whole interface should be either oneway or not, and we're
+ // gradually converting the methods (as some of them return a value form the server and must be
+ // refactored).
interface IAutoFillManager {
// Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
int addClient(in IAutoFillManagerClient client, int userId);
@@ -43,13 +46,14 @@
in ComponentName componentName, boolean compatMode);
FillEventHistory getFillEventHistory();
boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
- void updateSession(int sessionId, in AutofillId id, in Rect bounds,
+ oneway void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int action, int flags, int userId);
- void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
- void finishSession(int sessionId, int userId);
- void cancelSession(int sessionId, int userId);
- void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
- void setHasCallback(int sessionId, int userId, boolean hasIt);
+ oneway void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
+ oneway void finishSession(int sessionId, int userId);
+ oneway void cancelSession(int sessionId, int userId);
+ oneway void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId,
+ int userId);
+ oneway void setHasCallback(int sessionId, int userId, boolean hasIt);
void disableOwnedAutofillServices(int userId);
boolean isServiceSupported(int userId);
boolean isServiceEnabled(int userId, String packageName);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7e6af49..bb93af5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -798,10 +798,18 @@
}
/** @hide */
- public void setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis,
- int backDisposition) {
+ public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
try {
- mService.setImeWindowStatus(imeToken, startInputToken, vis, backDisposition);
+ mService.setImeWindowStatus(imeToken, vis, backDisposition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ public void reportStartInput(IBinder imeToken, IBinder startInputToken) {
+ try {
+ mService.reportStartInput(imeToken, startInputToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1239,7 +1247,7 @@
}
boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
- IBinder windowGainingFocus, int controlFlags, int softInputMode,
+ @Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
final View view;
synchronized (mH) {
@@ -1256,6 +1264,20 @@
}
}
+ if (windowGainingFocus == null) {
+ windowGainingFocus = view.getWindowToken();
+ if (windowGainingFocus == null) {
+ Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
+ return false;
+ }
+ controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
+ if (view.onCheckIsTextEditor()) {
+ controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
+ }
+ softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
+ windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
+ }
+
// Now we need to get an input connection from the served view.
// This is complicated in a couple ways: we can't be holding our lock
// when calling out to the view, and we need to make sure we call into
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 21ec42b..6644a85 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -222,8 +222,7 @@
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
boolean allowImplicitlySelectedSubtype) {
try {
- // Passing null as a locale until we support multiple enabled spell checker subtypes.
- return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
+ return mService.getCurrentSpellCheckerSubtype(allowImplicitlySelectedSubtype);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index 0c34e3c..4d3bbe4 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -57,7 +57,8 @@
/**
* Clone the entire object to be used in the UI thread by clients of
* WebView. This creates a copy that should never be modified by any of the
- * webkit package classes.
+ * webkit package classes. On Android 4.4 and later there is no need to use
+ * this, as the object is already a read-only copy of the internal state.
*/
protected abstract WebBackForwardList clone();
}
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 74db039..b9e7042 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -23,7 +23,7 @@
/**
* A convenience class for accessing fields in an entry in the back/forward list
* of a WebView. Each WebHistoryItem is a snapshot of the requested history
- * item. Each history item may be updated during the load of a page.
+ * item.
* @see WebBackForwardList
*/
public abstract class WebHistoryItem implements Cloneable {
@@ -44,8 +44,6 @@
* history item. See getTargetUrl() for the url that is the actual target of
* this history item.
* @return The base url of this history item.
- * Note: The VM ensures 32-bit atomic read/write operations so we don't have
- * to synchronize this method.
*/
public abstract String getUrl();
@@ -60,22 +58,20 @@
/**
* Return the document title of this history item.
* @return The document title of this history item.
- * Note: The VM ensures 32-bit atomic read/write operations so we don't have
- * to synchronize this method.
*/
public abstract String getTitle();
/**
* Return the favicon of this history item or {@code null} if no favicon was found.
* @return A Bitmap containing the favicon for this history item or {@code null}.
- * Note: The VM ensures 32-bit atomic read/write operations so we don't have
- * to synchronize this method.
*/
@Nullable
public abstract Bitmap getFavicon();
/**
- * Clone the history item for use by clients of WebView.
+ * Clone the history item for use by clients of WebView. On Android 4.4 and later
+ * there is no need to use this, as the object is already a read-only copy of the
+ * internal state.
*/
protected abstract WebHistoryItem clone();
}
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 6df76fa..e6f948f 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -21,6 +21,7 @@
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
@@ -590,7 +591,8 @@
private void addPermToList(List<MyPermissionInfo> permList,
MyPermissionInfo pInfo) {
if (pInfo.mLabel == null) {
- pInfo.mLabel = pInfo.loadLabel(mPm);
+ pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
}
int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
@@ -611,7 +613,9 @@
}
MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
if (group != null) {
- pInfo.mLabel = pInfo.loadLabel(mPm);
+ pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000,
+ PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
addPermToList(group.mAllPermissions, pInfo);
if (pInfo.mNew) {
addPermToList(group.mNewPermissions, pInfo);
@@ -622,14 +626,18 @@
for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
- pgrp.mLabel = pgrp.loadLabel(mPm);
+ pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
} else {
ApplicationInfo app;
try {
app = mPm.getApplicationInfo(pgrp.packageName, 0);
- pgrp.mLabel = app.loadLabel(mPm);
+ pgrp.mLabel = app.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
} catch (NameNotFoundException e) {
- pgrp.mLabel = pgrp.loadLabel(mPm);
+ pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000,
+ PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
}
}
mPermGroupsList.add(pgrp);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 929496f..11054c8 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -611,7 +611,6 @@
mRenderer.destroy();
mSurfaceControl.destroy();
mSurfaceSession.kill();
- mBitmapRenderNode.destroy();
mHandler.removeCallbacks(mMagnifierUpdater);
if (mBitmap != null) {
mBitmap.recycle();
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b591163..f89a9d9 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -136,7 +136,7 @@
return null;
}
- String mimeType = getTypeForFile(file);
+ String mimeType = getDocumentType(documentId);
if (!MetadataReader.isSupportedMimeType(mimeType)) {
return null;
}
@@ -418,7 +418,19 @@
@Override
public String getDocumentType(String documentId) throws FileNotFoundException {
final File file = getFileForDocId(documentId);
- return getTypeForFile(file);
+ if (file.isDirectory()) {
+ return Document.MIME_TYPE_DIR;
+ } else {
+ final int lastDot = documentId.lastIndexOf('.');
+ if (lastDot >= 0) {
+ final String extension = documentId.substring(lastDot + 1).toLowerCase();
+ final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ if (mime != null) {
+ return mime;
+ }
+ }
+ return MIMETYPE_OCTET_STREAM;
+ }
}
@Override
@@ -483,7 +495,7 @@
}
}
- final String mimeType = getTypeForFile(file);
+ final String mimeType = getDocumentType(docId);
final String displayName = file.getName();
if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
@@ -510,31 +522,10 @@
return row;
}
- private static String getTypeForFile(File file) {
- if (file.isDirectory()) {
- return Document.MIME_TYPE_DIR;
- } else {
- return getTypeForName(file.getName());
- }
- }
-
protected boolean typeSupportsMetadata(String mimeType) {
return MetadataReader.isSupportedMimeType(mimeType);
}
- private static String getTypeForName(String name) {
- final int lastDot = name.lastIndexOf('.');
- if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1).toLowerCase();
- final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if (mime != null) {
- return mime;
- }
- }
-
- return MIMETYPE_OCTET_STREAM;
- }
-
protected final File getFileForDocId(String docId) throws FileNotFoundException {
return getFileForDocId(docId, false);
}
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index c5940ba..20f2aa0 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -30,8 +30,7 @@
*/
interface ITextServicesManager {
SpellCheckerInfo getCurrentSpellChecker(String locale);
- SpellCheckerSubtype getCurrentSpellCheckerSubtype(
- String locale, boolean allowImplicitlySelectedSubtype);
+ SpellCheckerSubtype getCurrentSpellCheckerSubtype(boolean allowImplicitlySelectedSubtype);
oneway void getSpellCheckerService(String sciId, in String locale,
in ITextServicesSessionListener tsListener,
in ISpellCheckerSessionListener scListener, in Bundle bundle);
diff --git a/core/java/com/android/internal/util/HexDump.java b/core/java/com/android/internal/util/HexDump.java
index 7be95d8..af00400 100644
--- a/core/java/com/android/internal/util/HexDump.java
+++ b/core/java/com/android/internal/util/HexDump.java
@@ -16,18 +16,21 @@
package com.android.internal.util;
+import android.annotation.Nullable;
+
public class HexDump
{
private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
private final static char[] HEX_LOWER_CASE_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- public static String dumpHexString(byte[] array)
- {
+ public static String dumpHexString(@Nullable byte[] array) {
+ if (array == null) return "(null)";
return dumpHexString(array, 0, array.length);
}
- public static String dumpHexString(byte[] array, int offset, int length)
+ public static String dumpHexString(@Nullable byte[] array, int offset, int length)
{
+ if (array == null) return "(null)";
StringBuilder result = new StringBuilder();
byte[] line = new byte[16];
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 6c3a58c..989c58b 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -79,6 +79,11 @@
*/
public static final int ACTION_ROTATE_SCREEN = 6;
+ /*
+ * Time between we get a face acquired signal until we start with the unlock animation
+ */
+ public static final int ACTION_FACE_WAKE_AND_UNLOCK = 6;
+
private static final String[] NAMES = new String[] {
"expand panel",
"toggle recents",
@@ -86,7 +91,8 @@
"check credential",
"check credential unlocked",
"turn on screen",
- "rotate the screen"};
+ "rotate the screen",
+ "face wake-and-unlock" };
private static LatencyTracker sLatencyTracker;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9ed1ffb..f76eddf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -74,8 +74,8 @@
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
- void setImeWindowStatus(in IBinder token, in IBinder startInputToken, int vis,
- int backDisposition);
+ void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+ void reportStartInput(in IBinder token, in IBinder startInputToken);
void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
InputMethodSubtype getCurrentInputMethodSubtype();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d294933..80d8063 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1528,7 +1528,7 @@
* @see StrongAuthTracker#isFingerprintAllowedForUser
*/
public boolean isFingerprintAllowedForUser(int userId) {
- return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0;
+ return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0;
}
public boolean isUserInLockdown(int userId) {
@@ -1733,11 +1733,10 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN = 0x20;
/**
- * Strong auth flags that do not prevent fingerprint from being accepted as auth.
- *
- * If any other flags are set, fingerprint is disabled.
+ * Strong auth flags that do not prevent biometric methods from being accepted as auth.
+ * If any other flags are set, biometric authentication is disabled.
*/
- private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
+ private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED
| SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
@@ -1784,11 +1783,11 @@
}
/**
- * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
- * current strong authentication requirements.
+ * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+ * by the current strong authentication requirements.
*/
- public boolean isFingerprintAllowedForUser(int userId) {
- return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
+ public boolean isBiometricAllowedForUser(int userId) {
+ return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0;
}
/**
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 514874b..0f2e9c5 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -60,6 +60,13 @@
public class MessagingLayout extends FrameLayout {
private static final float COLOR_SHIFT_AMOUNT = 60;
+ /**
+ * Pattren for filter some ingonable characters.
+ * p{Z} for any kind of whitespace or invisible separator.
+ * p{C} for any kind of punctuation character.
+ */
+ private static final Pattern IGNORABLE_CHAR_PATTERN
+ = Pattern.compile("[\\p{C}\\p{Z}]");
private static final Pattern SPECIAL_CHAR_PATTERN
= Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
private static final Consumer<MessagingMessage> REMOVE_MESSAGE
@@ -233,7 +240,10 @@
continue;
}
if (!uniqueNames.containsKey(senderName)) {
- char c = senderName.charAt(0);
+ // Only use visible characters to get uniqueNames
+ String pureSenderName = IGNORABLE_CHAR_PATTERN
+ .matcher(senderName).replaceAll("" /* replacement */);
+ char c = pureSenderName.charAt(0);
if (uniqueCharacters.containsKey(c)) {
// this character was already used, lets make it more unique. We first need to
// resolve the existing character if it exists
@@ -245,7 +255,7 @@
uniqueNames.put(senderName, findNameSplit((String) senderName));
} else {
uniqueNames.put(senderName, Character.toString(c));
- uniqueCharacters.put(c, senderName);
+ uniqueCharacters.put(c, pureSenderName);
}
}
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index bf183cc..638b3ea 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -934,146 +934,6 @@
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
-
-/* pulled out of bionic */
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
- size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
-extern "C" void free_malloc_leak_info(uint8_t* info);
-#define SIZE_FLAG_ZYGOTE_CHILD (1<<31)
-
-static size_t gNumBacktraceElements;
-
-/*
- * This is a qsort() callback.
- *
- * See dumpNativeHeap() for comments about the data format and sort order.
- */
-static int compareHeapRecords(const void* vrec1, const void* vrec2)
-{
- const size_t* rec1 = (const size_t*) vrec1;
- const size_t* rec2 = (const size_t*) vrec2;
- size_t size1 = *rec1;
- size_t size2 = *rec2;
-
- if (size1 < size2) {
- return 1;
- } else if (size1 > size2) {
- return -1;
- }
-
- uintptr_t* bt1 = (uintptr_t*)(rec1 + 2);
- uintptr_t* bt2 = (uintptr_t*)(rec2 + 2);
- for (size_t idx = 0; idx < gNumBacktraceElements; idx++) {
- uintptr_t addr1 = bt1[idx];
- uintptr_t addr2 = bt2[idx];
- if (addr1 == addr2) {
- if (addr1 == 0)
- break;
- continue;
- }
- if (addr1 < addr2) {
- return -1;
- } else if (addr1 > addr2) {
- return 1;
- }
- }
-
- return 0;
-}
-
-/*
- * The get_malloc_leak_info() call returns an array of structs that
- * look like this:
- *
- * size_t size
- * size_t allocations
- * intptr_t backtrace[32]
- *
- * "size" is the size of the allocation, "backtrace" is a fixed-size
- * array of function pointers, and "allocations" is the number of
- * allocations with the exact same size and backtrace.
- *
- * The entries are sorted by descending total size (i.e. size*allocations)
- * then allocation count. For best results with "diff" we'd like to sort
- * primarily by individual size then stack trace. Since the entries are
- * fixed-size, and we're allowed (by the current implementation) to mangle
- * them, we can do this in place.
- */
-static void dumpNativeHeap(FILE* fp)
-{
- uint8_t* info = NULL;
- size_t overallSize, infoSize, totalMemory, backtraceSize;
-
- get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
- &backtraceSize);
- if (info == NULL) {
- fprintf(fp, "Native heap dump not available. To enable, run these"
- " commands (requires root):\n");
- fprintf(fp, "# adb shell stop\n");
- fprintf(fp, "# adb shell setprop libc.debug.malloc.options "
- "backtrace\n");
- fprintf(fp, "# adb shell start\n");
- return;
- }
- assert(infoSize != 0);
- assert(overallSize % infoSize == 0);
-
- fprintf(fp, "Android Native Heap Dump v1.0\n\n");
-
- size_t recordCount = overallSize / infoSize;
- fprintf(fp, "Total memory: %zu\n", totalMemory);
- fprintf(fp, "Allocation records: %zd\n", recordCount);
- fprintf(fp, "Backtrace size: %zd\n", backtraceSize);
- fprintf(fp, "\n");
-
- /* re-sort the entries */
- gNumBacktraceElements = backtraceSize;
- qsort(info, recordCount, infoSize, compareHeapRecords);
-
- /* dump the entries to the file */
- const uint8_t* ptr = info;
- for (size_t idx = 0; idx < recordCount; idx++) {
- size_t size = *(size_t*) ptr;
- size_t allocations = *(size_t*) (ptr + sizeof(size_t));
- uintptr_t* backtrace = (uintptr_t*) (ptr + sizeof(size_t) * 2);
-
- fprintf(fp, "z %d sz %8zu num %4zu bt",
- (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
- size & ~SIZE_FLAG_ZYGOTE_CHILD,
- allocations);
- for (size_t bt = 0; bt < backtraceSize; bt++) {
- if (backtrace[bt] == 0) {
- break;
- } else {
-#ifdef __LP64__
- fprintf(fp, " %016" PRIxPTR, backtrace[bt]);
-#else
- fprintf(fp, " %08" PRIxPTR, backtrace[bt]);
-#endif
- }
- }
- fprintf(fp, "\n");
-
- ptr += infoSize;
- }
-
- free_malloc_leak_info(info);
-
- fprintf(fp, "MAPS\n");
- const char* maps = "/proc/self/maps";
- UniqueFile in = MakeUniqueFile(maps, "re");
- if (in == nullptr) {
- fprintf(fp, "Could not open %s\n", maps);
- return;
- }
- char buf[BUFSIZ];
- while (size_t n = fread(buf, sizeof(char), BUFSIZ, in.get())) {
- fwrite(buf, sizeof(char), n, fp);
- }
-
- fprintf(fp, "END\n");
-}
-
static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
{
if (fileDescriptor == NULL) {
@@ -1104,6 +964,9 @@
return true;
}
+/* pulled out of bionic */
+extern "C" void write_malloc_leak_info(FILE* fp);
+
/*
* Dump the native heap, writing human-readable output to the specified
* file descriptor.
@@ -1117,7 +980,9 @@
}
ALOGD("Native heap dump starting...\n");
- dumpNativeHeap(fp.get());
+ // Formatting of the native heap dump is handled by malloc debug itself.
+ // See https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md#backtrace-heap-dump-format
+ write_malloc_leak_info(fp.get());
ALOGD("Native heap dump complete.\n");
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 37ea810..22bbc3c 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -431,6 +431,14 @@
return renderNode->stagingProperties().getPivotY();
}
+static jint android_view_RenderNode_getWidth(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getWidth();
+}
+
+static jint android_view_RenderNode_getHeight(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight();
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - Animations
// ----------------------------------------------------------------------------
@@ -648,6 +656,8 @@
{ "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX },
{ "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
+ { "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth },
+ { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
};
int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 12165d4..caf4e4b4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -535,17 +535,209 @@
return true;
}
+// Utility routine to specialize a zygote child process.
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+ jint runtime_flags, jobjectArray javaRlimits,
+ jlong permittedCapabilities, jlong effectiveCapabilities,
+ jint mount_external, jstring java_se_info, jstring java_se_name,
+ bool is_system_server, bool is_child_zygote, jstring instructionSet,
+ jstring dataDir) {
+ std::string error_msg;
+
+ auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
+ __attribute__ ((noreturn)) {
+ const char* se_name_c_str = nullptr;
+ std::unique_ptr<ScopedUtfChars> se_name;
+ if (java_se_name != nullptr) {
+ se_name.reset(new ScopedUtfChars(env, java_se_name));
+ se_name_c_str = se_name->c_str();
+ }
+ if (se_name_c_str == nullptr && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ const std::string& error_msg = (se_name_c_str == nullptr)
+ ? msg
+ : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
+ env->FatalError(error_msg.c_str());
+ __builtin_unreachable();
+ };
+
+ // Keep capabilities across UID change, unless we're staying root.
+ if (uid != 0) {
+ if (!EnableKeepCapabilities(&error_msg)) {
+ fail_fn(error_msg);
+ }
+ }
+
+ if (!SetInheritable(permittedCapabilities, &error_msg)) {
+ fail_fn(error_msg);
+ }
+ if (!DropCapabilitiesBoundingSet(&error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ bool use_native_bridge = !is_system_server && (instructionSet != NULL)
+ && android::NativeBridgeAvailable();
+ if (use_native_bridge) {
+ ScopedUtfChars isa_string(env, instructionSet);
+ use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
+ }
+ if (use_native_bridge && dataDir == NULL) {
+ // dataDir should never be null if we need to use a native bridge.
+ // In general, dataDir will never be null for normal applications. It can only happen in
+ // special cases (for isolated processes which are not associated with any app). These are
+ // launched by the framework and should not be emulated anyway.
+ use_native_bridge = false;
+ ALOGW("Native bridge will not be used because dataDir == NULL.");
+ }
+
+ if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
+ ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
+ if (errno == ENOTCONN || errno == EROFS) {
+ // When device is actively encrypting, we get ENOTCONN here
+ // since FUSE was mounted before the framework restarted.
+ // When encrypted device is booting, we get EROFS since
+ // FUSE hasn't been created yet by init.
+ // In either case, continue without external storage.
+ } else {
+ fail_fn(error_msg);
+ }
+ }
+
+ // If this zygote isn't root, it won't be able to create a process group,
+ // since the directory is owned by root.
+ if (!is_system_server && getuid() == 0) {
+ int rc = createProcessGroup(uid, getpid());
+ if (rc != 0) {
+ if (rc == -EROFS) {
+ ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+ } else {
+ ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0/*pid*/, strerror(-rc));
+ }
+ }
+ }
+
+ if (!SetGids(env, javaGids, &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (!SetRLimits(env, javaRlimits, &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (use_native_bridge) {
+ ScopedUtfChars isa_string(env, instructionSet);
+ ScopedUtfChars data_dir(env, dataDir);
+ android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
+ }
+
+ int rc = setresgid(gid, gid, gid);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+ }
+
+ // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+ // uid from 0, which clears capabilities. The other alternative is to call
+ // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146). As the result, privileged syscalls used below still need to be accessible in
+ // app process.
+ SetUpSeccompFilter(uid);
+
+ rc = setresuid(uid, uid, uid);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+ }
+
+ // The "dumpable" flag of a process, which controls core dump generation, is
+ // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+ // user or group ID changes. See proc(5) for possible values. In most cases,
+ // the value is 0, so core dumps are disabled for zygote children. However,
+ // when running in a Chrome OS container, the value is already set to 2,
+ // which allows the external crash reporter to collect all core dumps. Since
+ // only system crashes are interested, core dump is disabled for app
+ // processes. This also ensures compliance with CTS.
+ int dumpable = prctl(PR_GET_DUMPABLE);
+ if (dumpable == -1) {
+ ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+ }
+ if (dumpable == 2 && uid >= AID_APP) {
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+ }
+ }
+
+ if (NeedsNoRandomizeWorkaround()) {
+ // Work around ARM kernel ASLR lossage (http://b/5817320).
+ int old_personality = personality(0xffffffff);
+ int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+ if (new_personality == -1) {
+ ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+ }
+ }
+
+ if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
+ &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (!SetSchedulerPolicy(&error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ const char* se_info_c_str = NULL;
+ ScopedUtfChars* se_info = NULL;
+ if (java_se_info != NULL) {
+ se_info = new ScopedUtfChars(env, java_se_info);
+ se_info_c_str = se_info->c_str();
+ if (se_info_c_str == NULL) {
+ fail_fn("se_info_c_str == NULL");
+ }
+ }
+ const char* se_name_c_str = NULL;
+ ScopedUtfChars* se_name = NULL;
+ if (java_se_name != NULL) {
+ se_name = new ScopedUtfChars(env, java_se_name);
+ se_name_c_str = se_name->c_str();
+ if (se_name_c_str == NULL) {
+ fail_fn("se_name_c_str == NULL");
+ }
+ }
+ rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_c_str, se_name_c_str));
+ }
+
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_name_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ if (se_name_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
+
+ delete se_info;
+ delete se_name;
+
+ // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+ UnsetChldSignalHandler();
+
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+ is_system_server, is_child_zygote, instructionSet);
+ if (env->ExceptionCheck()) {
+ fail_fn("Error calling post fork hooks.");
+ }
+}
+
// Utility routine to fork zygote and specialize the child process.
-static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
- jint runtime_flags, jobjectArray javaRlimits,
- jlong permittedCapabilities, jlong effectiveCapabilities,
- jint mount_external,
- jstring java_se_info, jstring java_se_name,
- bool is_system_server, jintArray fdsToClose,
- jintArray fdsToIgnore, bool is_child_zygote,
- jstring instructionSet, jstring dataDir) {
+static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
+ jintArray fdsToClose, jintArray fdsToIgnore) {
SetSignalHandlers();
+ // Block SIGCHLD prior to fork.
sigset_t sigchld;
sigemptyset(&sigchld);
sigaddset(&sigchld, SIGCHLD);
@@ -602,6 +794,7 @@
pid_t pid = fork();
if (pid == 0) {
+ // The child process.
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
@@ -614,187 +807,11 @@
if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
fail_fn(error_msg);
}
+ }
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
-
- // Keep capabilities across UID change, unless we're staying root.
- if (uid != 0) {
- if (!EnableKeepCapabilities(&error_msg)) {
- fail_fn(error_msg);
- }
- }
-
- if (!SetInheritable(permittedCapabilities, &error_msg)) {
- fail_fn(error_msg);
- }
- if (!DropCapabilitiesBoundingSet(&error_msg)) {
- fail_fn(error_msg);
- }
-
- bool use_native_bridge = !is_system_server && (instructionSet != NULL)
- && android::NativeBridgeAvailable();
- if (use_native_bridge) {
- ScopedUtfChars isa_string(env, instructionSet);
- use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
- }
- if (use_native_bridge && dataDir == NULL) {
- // dataDir should never be null if we need to use a native bridge.
- // In general, dataDir will never be null for normal applications. It can only happen in
- // special cases (for isolated processes which are not associated with any app). These are
- // launched by the framework and should not be emulated anyway.
- use_native_bridge = false;
- ALOGW("Native bridge will not be used because dataDir == NULL.");
- }
-
- if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
- ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
- if (errno == ENOTCONN || errno == EROFS) {
- // When device is actively encrypting, we get ENOTCONN here
- // since FUSE was mounted before the framework restarted.
- // When encrypted device is booting, we get EROFS since
- // FUSE hasn't been created yet by init.
- // In either case, continue without external storage.
- } else {
- fail_fn(error_msg);
- }
- }
-
- // If this zygote isn't root, it won't be able to create a process group,
- // since the directory is owned by root.
- if (!is_system_server && getuid() == 0) {
- int rc = createProcessGroup(uid, getpid());
- if (rc != 0) {
- if (rc == -EROFS) {
- ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
- } else {
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
- }
- }
- }
-
- std::string error_msg;
- if (!SetGids(env, javaGids, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetRLimits(env, javaRlimits, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (use_native_bridge) {
- ScopedUtfChars isa_string(env, instructionSet);
- ScopedUtfChars data_dir(env, dataDir);
- android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
- }
-
- int rc = setresgid(gid, gid, gid);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
- }
-
- // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
- // uid from 0, which clears capabilities. The other alternative is to call
- // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
- // b/71859146). As the result, privileged syscalls used below still need to be accessible in
- // app process.
- SetUpSeccompFilter(uid);
-
- rc = setresuid(uid, uid, uid);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
- }
-
- // The "dumpable" flag of a process, which controls core dump generation, is
- // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
- // user or group ID changes. See proc(5) for possible values. In most cases,
- // the value is 0, so core dumps are disabled for zygote children. However,
- // when running in a Chrome OS container, the value is already set to 2,
- // which allows the external crash reporter to collect all core dumps. Since
- // only system crashes are interested, core dump is disabled for app
- // processes. This also ensures compliance with CTS.
- int dumpable = prctl(PR_GET_DUMPABLE);
- if (dumpable == -1) {
- ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
- }
- if (dumpable == 2 && uid >= AID_APP) {
- if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
- ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
- }
- }
-
- if (NeedsNoRandomizeWorkaround()) {
- // Work around ARM kernel ASLR lossage (http://b/5817320).
- int old_personality = personality(0xffffffff);
- int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
- if (new_personality == -1) {
- ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
- }
- }
-
- if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
- &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetSchedulerPolicy(&error_msg)) {
- fail_fn(error_msg);
- }
-
- const char* se_info_c_str = NULL;
- ScopedUtfChars* se_info = NULL;
- if (java_se_info != NULL) {
- se_info = new ScopedUtfChars(env, java_se_info);
- se_info_c_str = se_info->c_str();
- if (se_info_c_str == NULL) {
- fail_fn("se_info_c_str == NULL");
- }
- }
- const char* se_name_c_str = NULL;
- ScopedUtfChars* se_name = NULL;
- if (java_se_name != NULL) {
- se_name = new ScopedUtfChars(env, java_se_name);
- se_name_c_str = se_name->c_str();
- if (se_name_c_str == NULL) {
- fail_fn("se_name_c_str == NULL");
- }
- }
- rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
- is_system_server, se_info_c_str, se_name_c_str));
- }
-
- // Make it easier to debug audit logs by setting the main thread's name to the
- // nice name rather than "app_process".
- if (se_name_c_str == NULL && is_system_server) {
- se_name_c_str = "system_server";
- }
- if (se_name_c_str != NULL) {
- SetThreadName(se_name_c_str);
- }
-
- delete se_info;
- delete se_name;
-
- // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
- UnsetChldSignalHandler();
-
- env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
- is_system_server, is_child_zygote, instructionSet);
- if (env->ExceptionCheck()) {
- fail_fn("Error calling post fork hooks.");
- }
- } else if (pid > 0) {
- // the parent process
-
- // We blocked SIGCHLD prior to a fork, we unblock it here.
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
+ // We blocked SIGCHLD prior to a fork, we unblock it here.
+ if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
}
return pid;
}
@@ -881,22 +898,27 @@
// available.
capabilities &= GetEffectiveCapabilityMask(env);
- return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
- rlimits, capabilities, capabilities, mount_external, se_info,
- se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
- instructionSet, appDataDir);
+ pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
+ if (pid == 0) {
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ capabilities, capabilities,
+ mount_external, se_info, se_name, false,
+ is_child_zygote == JNI_TRUE, instructionSet, appDataDir);
+ }
+ return pid;
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
- pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
- runtime_flags, rlimits,
- permittedCapabilities, effectiveCapabilities,
- MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
- NULL, false, NULL, NULL);
- if (pid > 0) {
+ pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
+ if (pid == 0) {
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ permittedCapabilities, effectiveCapabilities,
+ MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
+ false, NULL, NULL);
+ } else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
gSystemServerPid = pid;
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 39e65ca..1d299f5 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -182,7 +182,7 @@
];
// System Services
- optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
+ optional com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
(section).type = SECTION_DUMPSYS,
(section).args = "fingerprint --proto --incident"
];
diff --git a/core/proto/android/server/fingerprint.proto b/core/proto/android/server/fingerprint.proto
index 2a7fbc3..c5eb85c 100644
--- a/core/proto/android/server/fingerprint.proto
+++ b/core/proto/android/server/fingerprint.proto
@@ -15,7 +15,7 @@
*/
syntax = "proto2";
-package com.android.server.fingerprint;
+package com.android.server.biometrics.fingerprint;
import "frameworks/base/libs/incident/proto/android/privacy.proto";
@@ -46,7 +46,7 @@
optional PerformanceStatsProto crypto = 4;
}
-// A com.android.server.fingerprint.FingerpintService.PerformanceStats object.
+// A com.android.server.biometrics.fingerprint.FingerpintService.PerformanceStats object.
message PerformanceStatsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/service/face.proto b/core/proto/android/service/face.proto
new file mode 100644
index 0000000..4188a34
--- /dev/null
+++ b/core/proto/android/service/face.proto
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.service.face;
+
+option java_multiple_files = true;
+option java_outer_classname = "FaceServiceProto";
+
+message FaceServiceDumpProto {
+ // Each log may include multiple user_id for different users.
+ repeated FaceUserStatsProto users = 1;
+}
+
+message FaceUserStatsProto {
+ // Refer to the UserHandle documentation.
+ int32 user_id = 1;
+
+ // Normal face authentications stats (e.g. lockscreen).
+ FaceActionStatsProto normal = 2;
+
+ // Crypto authentications (e.g. to unlock password storage, make secure
+ // purchases, etc).
+ FaceActionStatsProto crypto = 3;
+}
+
+message FaceActionStatsProto {
+ // Number of accepted faces.
+ int32 accept = 1;
+
+ // Number of rejected faces.
+ int32 reject = 2;
+
+ // Total number of acquisitions. Should be >= accept+reject due to poor
+ // image acquisition in some cases (too high, too low, poor gaze, etc.)
+ int32 acquire = 3;
+
+ // Total number of lockouts.
+ int32 lockout = 4;
+
+ // Total number of permanent lockouts.
+ int32 lockout_permanent = 5;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e33ecb8..f4b7ec8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -262,6 +262,7 @@
<protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
<protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
+ <protected-broadcast android:name="android.media.action.MICROPHONE_MUTE_CHANGED" />
<protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
<protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
@@ -598,6 +599,8 @@
<protected-broadcast android:name="android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE" />
<protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" />
<protected-broadcast android:name="android.app.action.STATSD_STARTED" />
+ <protected-broadcast android:name="com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET" />
+ <protected-broadcast android:name="com.android.server.biometrics.face.ACTION_LOCKOUT_RESET" />
<!-- For IdleController -->
<protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
@@ -821,6 +824,9 @@
android:label="@string/permgrouplab_location"
android:description="@string/permgroupdesc_location"
android:request="@string/permgrouprequest_location"
+ android:requestDetail="@string/permgrouprequestdetail_location"
+ android:backgroundRequest="@string/permgroupbackgroundrequest_location"
+ android:backgroundRequestDetail="@string/permgroupbackgroundrequestdetail_location"
android:priority="400" />
<!-- Allows an app to access precise location.
@@ -831,6 +837,7 @@
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
+ android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access approximate location.
@@ -841,6 +848,7 @@
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
+ android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access location in the background. If you
@@ -2251,7 +2259,8 @@
android:description="@string/permdesc_install_shortcut"
android:protectionLevel="normal"/>
- <!--This permission is no longer supported.
+ <!-- <p class="caution"><strong>Don't use this permission in your app.</strong><br>This
+ permission is no longer supported.
-->
<permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
android:label="@string/permlab_uninstall_shortcut"
@@ -3629,6 +3638,14 @@
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signature" />
+ <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_FACE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
+ <permission android:name="android.permission.RESET_FACE_LOCKOUT"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.CONTROL_KEYGUARD"
@@ -3989,6 +4006,11 @@
<permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
android:protectionLevel="signature" />
+ <!-- Allows an application to read emergency info name.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="com.android.emergency.permission.READ_EMERGENCY_INFO_NAME"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c4fa190..4c96c1b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1580,6 +1580,7 @@
<attr name="banner" />
<attr name="logo" />
<attr name="permissionGroup" />
+ <attr name="backgroundPermission" />
<attr name="description" />
<attr name="request" />
<attr name="protectionLevel" />
@@ -1610,6 +1611,9 @@
<attr name="logo" />
<attr name="description" />
<attr name="request" />
+ <attr name="requestDetail" />
+ <attr name="backgroundRequest" />
+ <attr name="backgroundRequestDetail" />
<attr name="permissionGroupFlags" />
<attr name="priority" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c8b60d..408a4d0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -491,13 +491,21 @@
Note also: the order of this is important. The first upstream type
for which a satisfying network exists is used.
- -->
+ -->
<integer-array translatable="false" name="config_tether_upstream_types">
<item>1</item>
<item>7</item>
<item>0</item>
</integer-array>
+ <!-- When true, the tethering upstream network follows the current default
+ Internet network (except when the current default network is mobile,
+ in which case a DUN network will be used if required).
+
+ When true, overrides the config_tether_upstream_types setting above.
+ -->
+ <bool translatable="false" name="config_tether_upstream_automatic">false</bool>
+
<!-- If the DUN connection for this CDMA device supports more than just DUN -->
<!-- traffic you should list them here. -->
<!-- If this device is not CDMA this is ignored. If this list is empty on -->
@@ -659,9 +667,27 @@
<!-- Boolean indicating that wifi only link configuratios that have exact same credentials (i.e PSK) -->
<bool translatable="false" name="config_wifi_only_link_same_credential_configurations">true</bool>
- <!-- Boolean indicating whether framework needs to set the tx power limit for meeting SAR requirements
- during voice calls -->
- <bool translatable="false" name="config_wifi_framework_enable_voice_call_sar_tx_power_limit">false</bool>
+ <!-- Boolean indicating whether framework needs to set the tx power limit for meeting SAR requirements -->
+ <bool translatable="false" name="config_wifi_framework_enable_sar_tx_power_limit">false</bool>
+
+ <!-- Boolean indicating whether framework needs to use body proximity to set the tx power limit
+ for meeting SAR requirements -->
+ <bool translatable="false" name="config_wifi_framework_enable_body_proximity_sar_tx_power_limit">false</bool>
+
+ <!-- String for the sensor type for body/head proximity for SAR -->
+ <string translatable="false" name="config_wifi_sar_sensor_type"></string>
+
+ <!-- Integer indicating event id by sar sensor for free space -->
+ <integer translatable="false" name="config_wifi_framework_sar_free_space_event_id">1</integer>
+
+ <!-- Integer indicating event id by sar sensor for near hand -->
+ <integer translatable="false" name="config_wifi_framework_sar_near_hand_event_id">2</integer>
+
+ <!-- Integer indicating event id by sar sensor for near head -->
+ <integer translatable="false" name="config_wifi_framework_sar_near_head_event_id">3</integer>
+
+ <!-- Integer indicating event id by sar sensor for near body -->
+ <integer translatable="false" name="config_wifi_framework_sar_near_body_event_id">4</integer>
<!-- Wifi driver supports batched scan -->
<bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
@@ -3453,4 +3479,6 @@
<!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
<bool name="config_swipe_up_gesture_setting_available">false</bool>
+ <!-- Whether or not we should show the option to show battery percentage -->
+ <bool name="config_battery_percentage_setting_available">true</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9dccc88..4e96671 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -682,6 +682,13 @@
<!-- Message shown to the user when the apps requests permission from this group -->
<string name="permgrouprequest_location">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access this device\'s location?</string>
+ <!-- Subtitle of the message shown to the user when the apps requests permission to use the location only while app is in foreground [CHAR LIMIT=none]-->
+ <string name="permgrouprequestdetail_location">The app will only have access to the location while you\u2019re using the app.</string>
+ <!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background [CHAR LIMIT=60] -->
+ <string name="permgroupbackgroundrequest_location">Always allow
+ <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access this device\u2019s location?</string>
+ <!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background [CHAR LIMIT=none] -->
+ <string name="permgroupbackgroundrequestdetail_location">The app will always have access to the location, even when you\u2019re not using the app.</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_calendar">Calendar</string>
@@ -1401,6 +1408,74 @@
<!-- Content description which should be used for the fingerprint icon. -->
<string name="fingerprint_icon_content_description">Fingerprint icon</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
+ <string name="permlab_manageFace">manage face authentication hardware</string>
+ <!-- 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_manageFace">Allows the app to invoke methods to add and delete facial templates for use.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
+ <string name="permlab_useFaceAuthentication">use face authentication hardware</string>
+ <!-- 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>
+
+ <!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
+ <string name="face_acquired_insufficient">Couldn\u2019t process face. Please try again.</string>
+ <!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_bright">Face is too bright. Please try in lower light.</string>
+ <!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_dark">Face is too dark. Please uncover light source.</string>
+ <!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_close">Please move sensor farther from face.</string>
+ <!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_far">Please bring sensor closer to face.</string>
+ <!-- Message shown during face acquisition when the user is too high relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_high">Please move sensor higher.</string>
+ <!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_low">Please move sensor lower.</string>
+ <!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_right">Please move sensor to the right.</string>
+ <!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_left">Please move sensor to the left.</string>
+ <!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_poor_gaze">Please look at the sensor.</string>
+ <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
+ <string name="face_acquired_not_detected">No face detected.</string>
+
+
+ <!-- Message shown during face acquisition when the face is not kept steady infront of device [CHAR LIMIT=50] -->
+ <string name="face_acquired_not_steady">Keep face steady infront of device.</string>
+ <!-- Array containing custom messages shown during face acquisition from vendor. Vendor is expected to add and translate these strings -->
+ <string-array name="face_acquired_vendor">
+ </string-array>
+
+ <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=50] -->
+ <string name="face_error_hw_not_available">Face hardware not available.</string>
+ <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
+ <string name="face_error_timeout">Face time out reached. Try again.</string>
+ <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=50] -->
+ <string name="face_error_no_space">Face can\u2019t be stored.</string>
+ <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
+ <string name="face_error_canceled">Face operation canceled.</string>
+ <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
+ <string name="face_error_lockout">Too many attempts. Try again later.</string>
+ <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
+ <string name="face_error_lockout_permanent">Too many attempts. Facial authentication disabled.</string>
+ <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
+ <string name="face_error_unable_to_process">Try again.</string>
+ <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
+ <string name="face_error_not_enrolled">No face enrolled.</string>
+ <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
+ <string name="face_error_hw_not_present">This device does not have a face authentication sensor</string>
+
+ <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
+ <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
+
+ <!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
+ <string-array name="face_error_vendor">
+ </string-array>
+
+ <!-- Content description which should be used for the face icon. [CHAR LIMIT=10] -->
+ <string name="face_icon_content_description">Face icon</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readSyncSettings">read sync settings</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3173,6 +3248,21 @@
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
+ <!-- A notification is shown when the user's softap config has been changed due to underlying
+ hardware restrictions. This is the notifications's title.
+ [CHAR_LIMIT=NONE] -->
+ <string name="wifi_softap_config_change">Changes to your hotspot settings</string>
+
+ <!-- A notification is shown when the user's softap config has been changed due to underlying
+ hardware restrictions. This is the notification's summary message.
+ [CHAR_LIMIT=NONE] -->
+ <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string>
+
+ <!-- A notification is shown when the user's softap config has been changed due to underlying
+ hardware restrictions. This is the notification's full message.
+ [CHAR_LIMIT=NONE] -->
+ <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string>
+
<!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's title. %1$s is the network type that the device switched to, e.g., cellular data. It is one of the strings in the network_switch_type_name array. -->
<string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9e9c431..c746f47 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -336,7 +336,13 @@
<java-symbol type="bool" name="config_wifi_framework_enable_associated_network_selection" />
<java-symbol type="bool" name="config_wifi_framework_use_single_radio_chain_scan_results_network_selection" />
<java-symbol type="bool" name="config_wifi_only_link_same_credential_configurations" />
- <java-symbol type="bool" name="config_wifi_framework_enable_voice_call_sar_tx_power_limit" />
+ <java-symbol type="bool" name="config_wifi_framework_enable_sar_tx_power_limit" />
+ <java-symbol type="bool" name="config_wifi_framework_enable_body_proximity_sar_tx_power_limit" />
+ <java-symbol type="string" name="config_wifi_sar_sensor_type" />
+ <java-symbol type="integer" name="config_wifi_framework_sar_free_space_event_id" />
+ <java-symbol type="integer" name="config_wifi_framework_sar_near_hand_event_id" />
+ <java-symbol type="integer" name="config_wifi_framework_sar_near_head_event_id" />
+ <java-symbol type="integer" name="config_wifi_framework_sar_near_body_event_id" />
<java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
<java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
<java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
@@ -1055,6 +1061,9 @@
<java-symbol type="string" name="network_switch_type_name_unknown" />
<java-symbol type="string" name="wifi_no_internet" />
<java-symbol type="string" name="wifi_no_internet_detailed" />
+ <java-symbol type="string" name="wifi_softap_config_change" />
+ <java-symbol type="string" name="wifi_softap_config_change_summary" />
+ <java-symbol type="string" name="wifi_softap_config_change_detailed" />
<java-symbol type="string" name="wifi_connect_alert_title" />
<java-symbol type="string" name="wifi_connect_alert_message" />
<java-symbol type="string" name="wifi_connect_default_application" />
@@ -1822,6 +1831,7 @@
<java-symbol type="array" name="config_tether_bluetooth_regexs" />
<java-symbol type="array" name="config_tether_dhcp_range" />
<java-symbol type="array" name="config_tether_upstream_types" />
+ <java-symbol type="bool" name="config_tether_upstream_automatic" />
<java-symbol type="array" name="config_tether_apndata" />
<java-symbol type="array" name="config_tether_usb_regexs" />
<java-symbol type="array" name="config_tether_wifi_regexs" />
@@ -2193,6 +2203,7 @@
<java-symbol type="string" name="ext_media_move_failure_message" />
<java-symbol type="style" name="Animation.RecentApplications" />
<java-symbol type="integer" name="dock_enter_exit_duration" />
+ <java-symbol type="bool" name="config_battery_percentage_setting_available" />
<!-- ImfTest -->
<java-symbol type="layout" name="auto_complete_list" />
@@ -2412,6 +2423,30 @@
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
<java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
+ <!-- Face authentication messages -->
+ <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" />
+ <java-symbol type="string" name="face_error_timeout" />
+ <java-symbol type="array" name="face_error_vendor" />
+ <java-symbol type="string" name="face_error_canceled" />
+ <java-symbol type="string" name="face_error_lockout" />
+ <java-symbol type="string" name="face_error_lockout_permanent" />
+ <java-symbol type="string" name="face_error_not_enrolled" />
+ <java-symbol type="string" name="face_error_hw_not_present" />
+ <java-symbol type="string" name="face_acquired_insufficient" />
+ <java-symbol type="string" name="face_acquired_too_bright" />
+ <java-symbol type="string" name="face_acquired_too_dark" />
+ <java-symbol type="string" name="face_acquired_too_close" />
+ <java-symbol type="string" name="face_acquired_too_far" />
+ <java-symbol type="string" name="face_acquired_too_high" />
+ <java-symbol type="string" name="face_acquired_too_low" />
+ <java-symbol type="string" name="face_acquired_too_right" />
+ <java-symbol type="string" name="face_acquired_too_left" />
+ <java-symbol type="string" name="face_acquired_poor_gaze" />
+ <java-symbol type="string" name="face_acquired_not_detected" />
+ <java-symbol type="array" name="face_acquired_vendor" />
+
<!-- From various Material changes -->
<java-symbol type="attr" name="titleTextAppearance" />
<java-symbol type="attr" name="subtitleTextAppearance" />
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
new file mode 100644
index 0000000..c8bc35c
--- /dev/null
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.Os;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class RedactingFileDescriptorTest {
+ private Context mContext;
+ private File mFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mFile = File.createTempFile("redacting", "dat");
+ try (FileOutputStream out = new FileOutputStream(mFile)) {
+ final byte[] buf = new byte[1_000_000];
+ Arrays.fill(buf, (byte) 64);
+ out.write(buf);
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mFile.delete();
+ }
+
+ @Test
+ public void testSingleByte() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor
+ .open(mContext, mFile, new long[] { 10, 11 }).getFileDescriptor();
+
+ final byte[] buf = new byte[1_000];
+ assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
+ for (int i = 0; i < buf.length; i++) {
+ if (i == 10) {
+ assertEquals(0, buf[i]);
+ } else {
+ assertEquals(64, buf[i]);
+ }
+ }
+ }
+
+ @Test
+ public void testRanges() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor
+ .open(mContext, mFile, new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 0, 0, 0 }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 100));
+ assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 195));
+ assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 64, 64, 64, 64, 64 }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 395));
+ assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 64, 64, 64, 64, 64 }, buf);
+ }
+
+ @Test
+ public void testEntireFile() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor
+ .open(mContext, mFile, new long[] { 0, 5_000_000 }).getFileDescriptor();
+
+ try (FileInputStream in = new FileInputStream(fd)) {
+ int val;
+ while ((val = in.read()) != -1) {
+ assertEquals(0, val);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 07606ea..6b3bd65 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -460,6 +460,8 @@
Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
Settings.Global.WIFI_COUNTRY_CODE,
+ Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD,
+ Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX,
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
Settings.Global.WIFI_DISPLAY_ON,
@@ -469,6 +471,8 @@
Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
Settings.Global.WIFI_FREQUENCY_BAND,
Settings.Global.WIFI_IDLE_MS,
+ Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
+ Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
Settings.Global.WIFI_NETWORK_SHOW_RSSI,
@@ -510,6 +514,8 @@
Settings.Secure.ANR_SHOW_BACKGROUND,
Settings.Secure.ASSISTANT,
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+ Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+ Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
new file mode 100644
index 0000000..93ad41f0
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewGroupTransientViewTest {
+
+ @Rule
+ public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+ private FrameLayout mBasePanel;
+ private ViewGroup mTestViewGroup;
+ private TestView mTestView;
+
+ @Before
+ public void setUp() {
+ final Activity activity = mActivityRule.getActivity();
+ mBasePanel = new FrameLayout(activity);
+ mTestViewGroup = new FrameLayout(activity);
+ mTestView = new TestView(activity);
+ activity.runOnUiThread(() -> activity.setContentView(mBasePanel));
+ }
+
+ @UiThreadTest
+ @Test
+ public void addAndRemove_inNonAttachedViewGroup_shouldNotAttachAndDetach() {
+ mTestViewGroup.addTransientView(mTestView, 0);
+ assertEquals(0, mTestView.mAttachedCount);
+
+ mTestViewGroup.removeTransientView(mTestView);
+ assertEquals(0, mTestView.mDetachedCount);
+ }
+
+ @UiThreadTest
+ @Test
+ public void addAndRemove_inAttachedViewGroup_shouldAttachAndDetachOnce() {
+ mBasePanel.addView(mTestViewGroup);
+ mTestViewGroup.addTransientView(mTestView, 0);
+ assertEquals(mTestView, mTestViewGroup.getTransientView(0));
+ assertEquals(1, mTestViewGroup.getTransientViewCount());
+ assertEquals(1, mTestView.mAttachedCount);
+
+ mBasePanel.removeView(mTestViewGroup);
+ mTestViewGroup.removeTransientView(mTestView);
+ assertEquals(null, mTestViewGroup.getTransientView(0));
+ assertEquals(0, mTestViewGroup.getTransientViewCount());
+ assertEquals(1, mTestView.mDetachedCount);
+ }
+
+ @UiThreadTest
+ @Test
+ public void addRemoveAdd_noException() {
+ mBasePanel.addView(mTestViewGroup);
+ mTestViewGroup.addTransientView(mTestView, 1);
+ mTestViewGroup.removeTransientView(mTestView);
+ mTestViewGroup.addTransientView(mTestView, 2);
+ }
+
+ @UiThreadTest
+ @Test
+ public void reAddBeforeRemove_shouldThrowException() {
+ mTestViewGroup.addView(mTestView);
+
+ try {
+ mTestViewGroup.addTransientView(mTestView, 0);
+ fail("Not allow to add as transient view before removing it");
+ } catch (IllegalStateException e) {
+ // Expected
+ }
+
+ mTestViewGroup.removeView(mTestView);
+ mTestViewGroup.addTransientView(mTestView, 0);
+ try {
+ mTestViewGroup.addTransientView(mTestView, 1);
+ fail("Not allow to add the same transient view again");
+ } catch (IllegalStateException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void drawTransientView() throws Exception {
+ // For view can be drawn if keyguard is active.
+ mActivityRule.getActivity().setShowWhenLocked(true);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ mTestView.mOnDraw = () -> latch.countDown();
+
+ mActivityRule.getActivity().runOnUiThread(() -> {
+ mBasePanel.addView(mTestViewGroup);
+ mTestViewGroup.addTransientView(mTestView, 0);
+ });
+
+ if (!latch.await(3, TimeUnit.SECONDS)) {
+ fail("Transient view does not draw");
+ }
+ }
+
+ private static class TestView extends View {
+ int mAttachedCount;
+ int mDetachedCount;
+ Runnable mOnDraw;
+
+ TestView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAttachedCount++;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mDetachedCount++;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mOnDraw != null) {
+ mOnDraw.run();
+ mOnDraw = null;
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
index 951e87a..359bd5e 100644
--- a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
@@ -25,4 +25,7 @@
assertEquals("ABCDEF", HexDump.toHexString(
new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
}
+ public void testNullArray() {
+ assertEquals("(null)", HexDump.toHexString(null));
+ }
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 1b816fe..c195a8e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,7 +142,7 @@
LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
SkAutoCanvasRestore acr2(canvas, shouldClip);
- canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix);
+ canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
if (shouldClip) {
clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
}
@@ -200,9 +200,7 @@
setViewProperties(properties, canvas, &alphaMultiplier);
}
SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
- if (displayList->containsProjectionReceiver()) {
- displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
- }
+ displayList->mParentMatrix = canvas->getTotalMatrix();
// TODO should we let the bound of the drawable do this for us?
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 6292a6c..dba97fe 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -55,6 +55,11 @@
if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
return;
}
+ SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mDisplayList->mParentMatrix);
+ canvas->concat(childNode->getRecordedMatrix());
childNode->forceDraw(canvas);
drawIndex++;
}
@@ -102,6 +107,11 @@
RenderNodeDrawable* childNode = zChildren[drawIndex];
SkASSERT(childNode);
+ SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
+ canvas->concat(childNode->getRecordedMatrix());
childNode->forceDraw(canvas);
drawIndex++;
@@ -153,10 +163,15 @@
}
SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
SkMatrix shadowMatrix;
- mat4 hwuiMatrix;
+ mat4 hwuiMatrix(caster->getRecordedMatrix());
// TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+ // applyViewPropertyTransforms gets the same matrix, which render nodes apply with
+ // RenderNodeDrawable::setViewProperties as a part if their draw.
caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
hwuiMatrix.copyTo(shadowMatrix);
canvas->concat(shadowMatrix);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 58b9242..6eff589 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -173,12 +173,12 @@
// node is drawn.
const Outline* mProjectedOutline = nullptr;
- // mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
- // pass. Render nodes that have a child receiver node, will store their matrix in
- // mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with
- // the
- // outline of their parent.
- SkMatrix mProjectedReceiverParentMatrix;
+ // mParentMatrix is set and valid when render node tree is traversed during the draw
+ // pass. Render nodes, which draw in a order different than recording order (e.g. nodes with a
+ // child receiver node or Z elevation), can use mParentMatrix to calculate the final transform
+ // without replaying the matrix transform OPs from the display list.
+ // Child receiver node will set the matrix and then clip with the outline of their parent.
+ SkMatrix mParentMatrix;
};
}; // namespace skiapipeline
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 62f820a..10edf73 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -31,44 +31,242 @@
namespace uirenderer {
namespace renderthread {
-#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F)
-#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F)
+#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
+#define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
+#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- if (!hasVkContext()) return;
-
mRenderThread.renderState().onVkContextDestroyed();
mRenderThread.setGrContext(nullptr);
if (VK_NULL_HANDLE != mCommandPool) {
- mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
+ mDestroyCommandPool(mDevice, mCommandPool, nullptr);
mCommandPool = VK_NULL_HANDLE;
}
- mBackendContext.reset();
-}
-void VulkanManager::initialize() {
- if (hasVkContext()) {
- return;
+ if (mDevice != VK_NULL_HANDLE) {
+ mDeviceWaitIdle(mDevice);
+ mDestroyDevice(mDevice, nullptr);
}
- auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
+ if (mInstance != VK_NULL_HANDLE) {
+ mDestroyInstance(mInstance, nullptr);
+ }
- mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
- &mPresentQueueIndex, canPresent));
- LOG_ALWAYS_FATAL_IF(!mBackendContext.get());
+ mGraphicsQueue = VK_NULL_HANDLE;
+ mPresentQueue = VK_NULL_HANDLE;
+ mDevice = VK_NULL_HANDLE;
+ mPhysicalDevice = VK_NULL_HANDLE;
+ mInstance = VK_NULL_HANDLE;
+}
- // Get all the addresses of needed vulkan functions
- VkInstance instance = mBackendContext->fInstance;
- VkDevice device = mBackendContext->fDevice;
- GET_PROC(CreateAndroidSurfaceKHR);
- GET_PROC(DestroySurfaceKHR);
- GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
- GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
- GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
- GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+bool VulkanManager::setupDevice(VkPhysicalDeviceFeatures& deviceFeatures) {
+ VkResult err;
+
+ constexpr VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
+ nullptr, // pNext
+ "android framework", // pApplicationName
+ 0, // applicationVersion
+ "android framework", // pEngineName
+ 0, // engineVerison
+ VK_MAKE_VERSION(1, 0, 0), // apiVersion
+ };
+
+ std::vector<const char*> instanceExtensions;
+ {
+ GET_PROC(EnumerateInstanceExtensionProperties);
+
+ uint32_t extensionCount = 0;
+ err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+ if (VK_SUCCESS != err) {
+ return false;
+ }
+ std::unique_ptr<VkExtensionProperties[]> extensions(
+ new VkExtensionProperties[extensionCount]);
+ err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.get());
+ if (VK_SUCCESS != err) {
+ return false;
+ }
+ bool hasKHRSurfaceExtension = false;
+ bool hasKHRAndroidSurfaceExtension = false;
+ for (uint32_t i = 0; i < extensionCount; ++i) {
+ instanceExtensions.push_back(extensions[i].extensionName);
+ if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
+ hasKHRSurfaceExtension = true;
+ }
+ if (!strcmp(extensions[i].extensionName,VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) {
+ hasKHRAndroidSurfaceExtension = true;
+ }
+ }
+ if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) {
+ this->destroy();
+ return false;
+ }
+ }
+
+ const VkInstanceCreateInfo instance_create = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // flags
+ &app_info, // pApplicationInfo
+ 0, // enabledLayerNameCount
+ nullptr, // ppEnabledLayerNames
+ (uint32_t) instanceExtensions.size(), // enabledExtensionNameCount
+ instanceExtensions.data(), // ppEnabledExtensionNames
+ };
+
+ GET_PROC(CreateInstance);
+ err = mCreateInstance(&instance_create, nullptr, &mInstance);
+ if (err < 0) {
+ this->destroy();
+ return false;
+ }
+
+ GET_INST_PROC(DestroyInstance);
+ GET_INST_PROC(EnumeratePhysicalDevices);
+ GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
+ GET_INST_PROC(GetPhysicalDeviceFeatures);
+ GET_INST_PROC(CreateDevice);
+ GET_INST_PROC(EnumerateDeviceExtensionProperties);
+ GET_INST_PROC(CreateAndroidSurfaceKHR);
+ GET_INST_PROC(DestroySurfaceKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+
+ uint32_t gpuCount;
+ err = mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr);
+ if (err) {
+ this->destroy();
+ return false;
+ }
+ if (!gpuCount) {
+ this->destroy();
+ return false;
+ }
+ // Just returning the first physical device instead of getting the whole array. Since there
+ // should only be one device on android.
+ gpuCount = 1;
+ err = mEnumeratePhysicalDevices(mInstance, &gpuCount, &mPhysicalDevice);
+ // VK_INCOMPLETE is returned when the count we provide is less than the total device count.
+ if (err && VK_INCOMPLETE != err) {
+ this->destroy();
+ return false;
+ }
+
+ // query to get the initial queue props size
+ uint32_t queueCount;
+ mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
+ if (!queueCount) {
+ this->destroy();
+ return false;
+ }
+
+ // now get the actual queue props
+ std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]);
+ mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get());
+
+ // iterate to find the graphics queue
+ mGraphicsQueueIndex = queueCount;
+ for (uint32_t i = 0; i < queueCount; i++) {
+ if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ mGraphicsQueueIndex = i;
+ break;
+ }
+ }
+ if (mGraphicsQueueIndex == queueCount) {
+ this->destroy();
+ return false;
+ }
+
+ // All physical devices and queue families on Android must be capable of
+ // presentation with any native window. So just use the first one.
+ mPresentQueueIndex = 0;
+
+ std::vector<const char*> deviceExtensions;
+ {
+ uint32_t extensionCount = 0;
+ err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+ nullptr);
+ if (VK_SUCCESS != err) {
+ this->destroy();
+ return false;
+ }
+ std::unique_ptr<VkExtensionProperties[]> extensions(
+ new VkExtensionProperties[extensionCount]);
+ err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+ extensions.get());
+ if (VK_SUCCESS != err) {
+ this->destroy();
+ return false;
+ }
+ bool hasKHRSwapchainExtension = false;
+ for (uint32_t i = 0; i < extensionCount; ++i) {
+ deviceExtensions.push_back(extensions[i].extensionName);
+ if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+ hasKHRSwapchainExtension = true;
+ }
+ }
+ if (!hasKHRSwapchainExtension) {
+ this->destroy();
+ return false;
+ }
+ }
+
+ // query to get the physical device properties
+ mGetPhysicalDeviceFeatures(mPhysicalDevice, &deviceFeatures);
+ // this looks like it would slow things down,
+ // and we can't depend on it on all platforms
+ deviceFeatures.robustBufferAccess = VK_FALSE;
+
+ float queuePriorities[1] = { 0.0 };
+
+ const VkDeviceQueueCreateInfo queueInfo[2] = {
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceQueueCreateFlags
+ mGraphicsQueueIndex, // queueFamilyIndex
+ 1, // queueCount
+ queuePriorities, // pQueuePriorities
+ },
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceQueueCreateFlags
+ mPresentQueueIndex, // queueFamilyIndex
+ 1, // queueCount
+ queuePriorities, // pQueuePriorities
+ }
+ };
+ uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1;
+
+ const VkDeviceCreateInfo deviceInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceCreateFlags
+ queueInfoCount, // queueCreateInfoCount
+ queueInfo, // pQueueCreateInfos
+ 0, // layerCount
+ nullptr, // ppEnabledLayerNames
+ (uint32_t) deviceExtensions.size(), // extensionCount
+ deviceExtensions.data(), // ppEnabledExtensionNames
+ &deviceFeatures // ppEnabledFeatures
+ };
+
+ err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice);
+ if (err) {
+ this->destroy();
+ return false;
+ }
+
+ GET_DEV_PROC(GetDeviceQueue);
+ GET_DEV_PROC(DeviceWaitIdle);
+ GET_DEV_PROC(DestroyDevice);
GET_DEV_PROC(CreateSwapchainKHR);
GET_DEV_PROC(DestroySwapchainKHR);
GET_DEV_PROC(GetSwapchainImagesKHR);
@@ -93,25 +291,76 @@
GET_DEV_PROC(WaitForFences);
GET_DEV_PROC(ResetFences);
+ return true;
+}
+
+void VulkanManager::initialize() {
+ if (mDevice != VK_NULL_HANDLE) {
+ return;
+ }
+
+ std::vector<const char*> instanceExtensions;
+ std::vector<const char*> deviceExtensions;
+ VkPhysicalDeviceFeatures deviceFeatures;
+ LOG_ALWAYS_FATAL_IF(!this->setupDevice(deviceFeatures));
+
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+
+ uint32_t extensionFlags = kKHR_surface_GrVkExtensionFlag |
+ kKHR_android_surface_GrVkExtensionFlag |
+ kKHR_swapchain_GrVkExtensionFlag;
+
+ uint32_t featureFlags = 0;
+ if (deviceFeatures.geometryShader) {
+ featureFlags |= kGeometryShader_GrVkFeatureFlag;
+ }
+ if (deviceFeatures.dualSrcBlend) {
+ featureFlags |= kDualSrcBlend_GrVkFeatureFlag;
+ }
+ if (deviceFeatures.sampleRateShading) {
+ featureFlags |= kSampleRateShading_GrVkFeatureFlag;
+ }
+
+ auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+ };
+ auto interface =
+ sk_make_sp<GrVkInterface>(getProc, mInstance, mDevice, extensionFlags);
+
+ GrVkBackendContext backendContext;
+ backendContext.fInstance = mInstance;
+ backendContext.fPhysicalDevice = mPhysicalDevice;
+ backendContext.fDevice = mDevice;
+ backendContext.fQueue = mGraphicsQueue;
+ backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+ backendContext.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
+ backendContext.fExtensions = extensionFlags;
+ backendContext.fFeatures = featureFlags;
+ backendContext.fInterface = std::move(interface);
+ backendContext.fOwnsInstanceAndDevice = false;
+
// create the command pool for the command buffers
if (VK_NULL_HANDLE == mCommandPool) {
VkCommandPoolCreateInfo commandPoolInfo;
memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
// this needs to be on the render queue
- commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
+ commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex;
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, &commandPoolInfo,
- nullptr, &mCommandPool);
+ SkDEBUGCODE(VkResult res =) mCreateCommandPool(mDevice, &commandPoolInfo, nullptr,
+ &mCommandPool);
SkASSERT(VK_SUCCESS == res);
}
- mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+ mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
mRenderThread.cacheManager().configureContext(&options);
- sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options));
+ sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
mRenderThread.setGrContext(grContext);
DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
@@ -138,8 +387,7 @@
// Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
// reuse its commands buffers.
- VkResult res =
- mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
+ VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
if (res != VK_SUCCESS) {
return nullptr;
}
@@ -153,12 +401,12 @@
VkResult res;
- res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
SkASSERT(VK_SUCCESS == res);
// The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
// finished presenting and that it is safe to begin sending new commands to the returned image.
- res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
&backbuffer->mImageIndex);
@@ -173,11 +421,11 @@
return nullptr;
}
backbuffer = getAvailableBackbuffer(surface);
- res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
SkASSERT(VK_SUCCESS == res);
// acquire the image
- res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
&backbuffer->mImageIndex);
@@ -205,7 +453,7 @@
layout, // oldLayout
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
mPresentQueueIndex, // srcQueueFamilyIndex
- mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex
+ mGraphicsQueueIndex, // dstQueueFamilyIndex
surface->mImages[backbuffer->mImageIndex], // image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange
};
@@ -236,7 +484,7 @@
submitInfo.signalSemaphoreCount = 0;
// Attach first fence to submission here so we can track when the command buffer finishes.
- mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
// We need to notify Skia that we changed the layout of the wrapped VkImage
sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
@@ -255,17 +503,14 @@
void VulkanManager::destroyBuffers(VulkanSurface* surface) {
if (surface->mBackbuffers) {
for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
- mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
- UINT64_MAX);
+ mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX);
surface->mBackbuffers[i].mImageIndex = -1;
- mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
- nullptr);
- mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
- nullptr);
- mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
- surface->mBackbuffers[i].mTransitionCmdBuffers);
- mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
- mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
+ mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr);
+ mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr);
+ mFreeCommandBuffers(mDevice, mCommandPool, 2,
+ surface->mBackbuffers[i].mTransitionCmdBuffers);
+ mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
+ mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
}
}
@@ -282,29 +527,27 @@
if (VK_NULL_HANDLE != mPresentQueue) {
mQueueWaitIdle(mPresentQueue);
}
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
destroyBuffers(surface);
if (VK_NULL_HANDLE != surface->mSwapchain) {
- mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
+ mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr);
surface->mSwapchain = VK_NULL_HANDLE;
}
if (VK_NULL_HANDLE != surface->mVkSurface) {
- mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
+ mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr);
surface->mVkSurface = VK_NULL_HANDLE;
}
delete surface;
}
void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
- mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
- nullptr);
+ mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr);
SkASSERT(surface->mImageCount);
surface->mImages = new VkImage[surface->mImageCount];
- mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
- surface->mImages);
+ mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
@@ -354,15 +597,15 @@
for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
SkDEBUGCODE(VkResult res);
surface->mBackbuffers[i].mImageIndex = -1;
- SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
&surface->mBackbuffers[i].mAcquireSemaphore);
- SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
&surface->mBackbuffers[i].mRenderSemaphore);
- SkDEBUGCODE(res =) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
+ SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo,
surface->mBackbuffers[i].mTransitionCmdBuffers);
- SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
&surface->mBackbuffers[i].mUsageFences[0]);
- SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
&surface->mBackbuffers[i].mUsageFences[1]);
SkASSERT(VK_SUCCESS == res);
}
@@ -372,35 +615,35 @@
bool VulkanManager::createSwapchain(VulkanSurface* surface) {
// check for capabilities
VkSurfaceCapabilitiesKHR caps;
- VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice,
+ VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
surface->mVkSurface, &caps);
if (VK_SUCCESS != res) {
return false;
}
uint32_t surfaceFormatCount;
- res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
&surfaceFormatCount, nullptr);
if (VK_SUCCESS != res) {
return false;
}
FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
- res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
&surfaceFormatCount, surfaceFormats.data());
if (VK_SUCCESS != res) {
return false;
}
uint32_t presentModeCount;
- res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
surface->mVkSurface, &presentModeCount, nullptr);
if (VK_SUCCESS != res) {
return false;
}
FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
- res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
surface->mVkSurface, &presentModeCount,
presentModes.data());
if (VK_SUCCESS != res) {
@@ -482,8 +725,8 @@
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = usageFlags;
- uint32_t queueFamilies[] = {mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex};
- if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) {
+ uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex};
+ if (mGraphicsQueueIndex != mPresentQueueIndex) {
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchainCreateInfo.queueFamilyIndexCount = 2;
swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
@@ -499,19 +742,18 @@
swapchainCreateInfo.clipped = true;
swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
- res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
- &surface->mSwapchain);
+ res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain);
if (VK_SUCCESS != res) {
return false;
}
// destroy the old swapchain
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
destroyBuffers(surface);
- mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
+ mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
}
createBuffers(surface, surfaceFormat, extent);
@@ -535,20 +777,18 @@
surfaceCreateInfo.flags = 0;
surfaceCreateInfo.window = window;
- VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, nullptr,
- &surface->mVkSurface);
+ VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr,
+ &surface->mVkSurface);
if (VK_SUCCESS != res) {
delete surface;
return nullptr;
}
SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR(
- mBackendContext->fPhysicalDevice, mPresentQueueIndex,
- surface->mVkSurface, &supported);
- // All physical devices and queue families on Android must be capable of
- // presentation with any
- // native window.
- SkASSERT(VK_SUCCESS == res && supported););
+ mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported);
+ // All physical devices and queue families on Android must be capable of
+ // presentation with any native window.
+ SkASSERT(VK_SUCCESS == res && supported););
if (!createSwapchain(surface)) {
destroySurface(surface);
@@ -605,7 +845,7 @@
void VulkanManager::swapBuffers(VulkanSurface* surface) {
if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
}
SkASSERT(surface->mBackbuffers);
@@ -638,7 +878,7 @@
dstAccessMask, // inputMask
layout, // oldLayout
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
- mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex
+ mGraphicsQueueIndex, // srcQueueFamilyIndex
mPresentQueueIndex, // dstQueueFamilyIndex
surface->mImages[backbuffer->mImageIndex], // image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange
@@ -670,7 +910,7 @@
submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
// Attach second fence to submission here so we can track when the command buffer finishes.
- mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
// Submit present operation to present queue. We use a semaphore here to make sure all rendering
// to the image is complete and that the layout has been change to present on the graphics
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index c319c9e..9a72706 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -79,7 +79,7 @@
void initialize();
// Quick check to see if the VulkanManager has been initialized.
- bool hasVkContext() { return mBackendContext.get() != nullptr; }
+ bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
// Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
// VulkanSurface object which is returned.
@@ -111,6 +111,10 @@
explicit VulkanManager(RenderThread& thread);
~VulkanManager() { destroy(); }
+ // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
+ // VkPhysicalDeviceFeatures struct.
+ bool setupDevice(VkPhysicalDeviceFeatures& deviceFeatures);
+
void destroyBuffers(VulkanSurface* surface);
bool createSwapchain(VulkanSurface* surface);
@@ -148,7 +152,21 @@
VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
- // Additional vulkan functions
+ // Instance Functions
+ VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
+ VkPtr<PFN_vkCreateInstance> mCreateInstance;
+
+ VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
+ VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+ VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
+ VkPtr<PFN_vkGetPhysicalDeviceFeatures> mGetPhysicalDeviceFeatures;
+ VkPtr<PFN_vkCreateDevice> mCreateDevice;
+ VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
+
+ // Device Functions
+ VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
+ VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
+ VkPtr<PFN_vkDestroyDevice> mDestroyDevice;
VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool;
VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool;
VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers;
@@ -158,10 +176,8 @@
VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer;
VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier;
- VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
VkPtr<PFN_vkQueueSubmit> mQueueSubmit;
VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle;
- VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
@@ -172,7 +188,12 @@
RenderThread& mRenderThread;
- sk_sp<const GrVkBackendContext> mBackendContext;
+ VkInstance mInstance = VK_NULL_HANDLE;
+ VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
+ VkDevice mDevice = VK_NULL_HANDLE;
+
+ uint32_t mGraphicsQueueIndex;
+ VkQueue mGraphicsQueue = VK_NULL_HANDLE;
uint32_t mPresentQueueIndex;
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 7f8cb2d..3d50d2d 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -127,7 +127,7 @@
return false;
}
void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
- if (!addr) {
+ if (addr == MAP_FAILED) {
int err = errno;
// The file not existing is normal for addToDump(), so only log if
// we get an unexpected error
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 15c0ab1..eb67e6c 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1094,7 +1094,7 @@
class ShadowTestCanvas : public SkCanvas {
public:
ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
- int getIndex() { return mDrawCounter; }
+ int getDrawCounter() { return mDrawCounter; }
virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
@@ -1109,17 +1109,36 @@
EXPECT_EQ(dy, TRANSLATE_Y);
}
- virtual void didConcat(const SkMatrix& matrix) override {
- // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
- // matrix.
+ virtual void didSetMatrix(const SkMatrix& matrix) override {
mDrawCounter++;
- EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
- EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
- getTotalMatrix());
+ // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+ // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+ EXPECT_TRUE(matrix.isIdentity());
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+
+ virtual void didConcat(const SkMatrix& matrix) override {
+ mDrawCounter++;
+ if (mFirstDidConcat) {
+ // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+ mFirstDidConcat = false;
+ EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+ matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+ getTotalMatrix());
+ } else {
+ // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
+ matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
+ getTotalMatrix());
+ }
}
protected:
int mDrawCounter = 0;
+ private:
+ bool mFirstDidConcat = true;
};
auto parent = TestUtils::createSkiaNode(
@@ -1143,7 +1162,7 @@
ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
RenderNodeDrawable drawable(parent.get(), &canvas, false);
canvas.drawDrawable(&drawable);
- EXPECT_EQ(6, canvas.getIndex());
+ EXPECT_EQ(9, canvas.getDrawCounter());
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
new file mode 100644
index 0000000..447195d
--- /dev/null
+++ b/location/lib/Android.bp
@@ -0,0 +1,21 @@
+//
+// 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.
+//
+
+java_sdk_library {
+ name: "com.android.location.provider",
+ srcs: ["java/**/*.java"],
+ api_packages: ["com.android.location.provider"],
+}
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
deleted file mode 100644
index 6642134..0000000
--- a/location/lib/Android.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Copyright (C) 2010 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.
-#
-LOCAL_PATH := $(call my-dir)
-
-# the library
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= com.android.location.provider
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-include $(BUILD_JAVA_LIBRARY)
-
-
-# ==== com.google.location.xml lib def ========================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := com.android.location.provider.xml
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_CLASS := ETC
-
-# This will install the file in /system/etc/permissions
-#
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-
-include $(BUILD_PREBUILT)
-
-# ==== Stub library ===========================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.location.provider-stubs-gen
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := $(call all-java-files-under,java)
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.location.provider.stubs_intermediates/src
-LOCAL_DROIDDOC_OPTIONS:= \
- -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
- -stubpackages com.android.location.provider \
- -nodocs
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_DROIDDOC)
-com_android_nfc_extras_gen_stamp := $(full_target)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.location.provider.stubs
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-LOCAL_SDK_VERSION := current
-LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_nfc_extras_gen_stamp)
-com_android_nfc_extras_gen_stamp :=
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
new file mode 100644
index 0000000..1e69f16
--- /dev/null
+++ b/location/lib/api/current.txt
@@ -0,0 +1,47 @@
+package com.android.location.provider {
+
+ public abstract deprecated class FusedProvider {
+ ctor public FusedProvider();
+ method public android.os.IBinder getBinder();
+ }
+
+ public abstract class LocationProviderBase {
+ ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
+ method public android.os.IBinder getBinder();
+ method public abstract void onDisable();
+ method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public abstract void onEnable();
+ method public abstract int onGetStatus(android.os.Bundle);
+ method public abstract long onGetStatusUpdateTime();
+ method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+ method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+ method public final void reportLocation(android.location.Location);
+ field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
+ field public static final java.lang.String FUSED_PROVIDER = "fused";
+ }
+
+ public final class LocationRequestUnbundled {
+ method public long getFastestInterval();
+ method public long getInterval();
+ method public int getQuality();
+ method public float getSmallestDisplacement();
+ field public static final int ACCURACY_BLOCK = 102; // 0x66
+ field public static final int ACCURACY_CITY = 104; // 0x68
+ field public static final int ACCURACY_FINE = 100; // 0x64
+ field public static final int POWER_HIGH = 203; // 0xcb
+ field public static final int POWER_LOW = 201; // 0xc9
+ field public static final int POWER_NONE = 200; // 0xc8
+ }
+
+ public final class ProviderPropertiesUnbundled {
+ method public static com.android.location.provider.ProviderPropertiesUnbundled create(boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
+ }
+
+ public final class ProviderRequestUnbundled {
+ method public long getInterval();
+ method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+ method public boolean getReportLocation();
+ }
+
+}
+
diff --git a/location/lib/api/removed.txt b/location/lib/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/removed.txt
diff --git a/location/lib/api/system-current.txt b/location/lib/api/system-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/system-current.txt
diff --git a/location/lib/api/system-removed.txt b/location/lib/api/system-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/system-removed.txt
diff --git a/location/lib/api/test-current.txt b/location/lib/api/test-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/test-current.txt
diff --git a/location/lib/api/test-removed.txt b/location/lib/api/test-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/location/lib/api/test-removed.txt
diff --git a/location/lib/com.android.location.provider.xml b/location/lib/com.android.location.provider.xml
deleted file mode 100644
index 000e68f..0000000
--- a/location/lib/com.android.location.provider.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<permissions>
- <library name="com.android.location.provider"
- file="/system/framework/com.android.location.provider.jar" />
-</permissions>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 196b886..1a282b2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -794,6 +794,11 @@
* <p>
* This method should only be used by applications that replace the platform-wide
* management of audio settings or the main telephony application.
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
+ * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
+ * unless the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
*
* @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
* {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
@@ -804,6 +809,8 @@
* @param flags One or more flags.
* @see #adjustVolume(int, int)
* @see #setStreamVolume(int, int, int)
+ * @throws SecurityException if the adjustment triggers a Do Not Disturb change
+ * and the caller is not granted notification policy access.
*/
public void adjustStreamVolume(int streamType, int direction, int flags) {
final IAudioService service = getService();
@@ -1121,6 +1128,8 @@
* @see #getStreamMaxVolume(int)
* @see #getStreamVolume(int)
* @see #isVolumeFixed()
+ * @throws SecurityException if the volume change triggers a Do Not Disturb change
+ * and the caller is not granted notification policy access.
*/
public void setStreamVolume(int streamType, int index, int flags) {
final IAudioService service = getService();
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index aae1f51..6bf52bd 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -323,8 +323,10 @@
StrictMode.setThreadPolicy(policy);
try {
- if (offset != mCurrentOffset) {
- seekTo(offset);
+ synchronized(this) {
+ if (offset != mCurrentOffset) {
+ seekTo(offset);
+ }
}
int n = mInputStream.read(data, 0, size);
@@ -366,7 +368,7 @@
}
@Override
- public long getSize() {
+ public synchronized long getSize() {
if (mConnection == null) {
try {
seekTo(0);
@@ -379,7 +381,7 @@
}
@Override
- public String getMIMEType() {
+ public synchronized String getMIMEType() {
if (mConnection == null) {
try {
seekTo(0);
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index dcc872c..70ef81f 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -116,9 +116,9 @@
* these circumstances. Sometimes, due to programming errors, invoking a playback
* control operation in an invalid state may also occur. Under all these
* error conditions, the internal player engine invokes a user supplied
- * MediaPlayer2EventCallback.onError() method if an MediaPlayer2EventCallback has been
+ * EventCallback.onError() method if an EventCallback has been
* registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ * {@link #setEventCallback(Executor, EventCallback)}.
* <ul>
* <li>It is important to note that once an error occurs, the
* MediaPlayer2 object enters the <em>Error</em> state (except as noted
@@ -159,9 +159,9 @@
* player engine continues working on the rest of preparation work
* until the preparation work completes. When the preparation completes,
* the internal player engine then calls a user supplied callback method,
- * onInfo() of the MediaPlayer2EventCallback interface with {@link #MEDIA_INFO_PREPARED},
- * if an MediaPlayer2EventCallback is registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
+ * onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED},
+ * if an EventCallback is registered beforehand via
+ * {@link #setEventCallback(Executor, EventCallback)}.</li>
* <li>It is important to note that
* the <em>Preparing</em> state is a transient state, and the behavior
* of calling any method with side effect while a MediaPlayer2 object is
@@ -180,10 +180,10 @@
* whether the MediaPlayer2 object is in the <em>Started</em> state.
* <ul>
* <li>While in the <em>Started</em> state, the internal player engine calls
- * a user supplied callback method MediaPlayer2EventCallback.onInfo() with
- * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an MediaPlayer2EventCallback has been
+ * a user supplied callback method EventCallback.onInfo() with
+ * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an EventCallback has been
* registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ * {@link #setEventCallback(Executor, EventCallback)}.
* This callback allows applications to keep track of the buffering status
* while streaming audio/video.</li>
* <li>Calling {@link #play()} has not effect
@@ -215,10 +215,10 @@
* call returns right away, the actual seek operation may take a while to
* finish, especially for audio/video being streamed. When the actual
* seek operation completes, the internal player engine calls a user
- * supplied MediaPlayer2EventCallback.onCallCompleted() with
+ * supplied EventCallback.onCallCompleted() with
* {@link #CALL_COMPLETED_SEEK_TO}
- * if an MediaPlayer2EventCallback has been registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
+ * if an EventCallback has been registered beforehand via
+ * {@link #setEventCallback(Executor, EventCallback)}.</li>
* <li>Please
* note that {@link #seekTo(long, int)} can also be called in the other states,
* such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
@@ -238,9 +238,9 @@
* the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
* <li>If the looping mode was set to <var>false
* </var>, the player engine calls a user supplied callback method,
- * MediaPlayer2EventCallback.onCompletion(), if an MediaPlayer2EventCallback is
+ * EventCallback.onCompletion(), if an EventCallback is
* registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ * {@link #setEventCallback(Executor, EventCallback)}.
* The invoke of the callback signals that the object is now in the <em>
* PlaybackCompleted</em> state.</li>
* <li>While in the <em>PlaybackCompleted</em>
@@ -387,7 +387,7 @@
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>setMediaPlayer2EventCallback </p></td>
+ * <tr><td>setEventCallback </p></td>
* <td>any </p></td>
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
@@ -445,7 +445,7 @@
* possible runtime errors during playback or streaming. Registration for
* these events is done by properly setting the appropriate listeners (via calls
* to
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)},
+ * {@link #setEventCallback(Executor, EventCallback)},
* {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
* In order to receive the respective callback
* associated with these listeners, applications are required to create
@@ -453,8 +453,8 @@
* thread by default has a Looper running).
*
*/
-public abstract class MediaPlayer2 extends MediaPlayerBase
- implements SubtitleController.Listener
+public abstract class MediaPlayer2 implements SubtitleController.Listener
+ , AutoCloseable
, AudioRouting {
/**
* Create a MediaPlayer2 object.
@@ -514,6 +514,12 @@
public MediaPlayer2() { }
/**
+ * Returns a {@link MediaPlayerBase} implementation which runs based on
+ * this MediaPlayer2 instance.
+ */
+ public abstract MediaPlayerBase getMediaPlayerBase();
+
+ /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
@@ -549,7 +555,6 @@
*
*/
// This is an asynchronous call.
- @Override
public abstract void play();
/**
@@ -560,21 +565,18 @@
*
*/
// This is an asynchronous call.
- @Override
public abstract void prepare();
/**
* Pauses playback. Call play() to resume.
*/
// This is an asynchronous call.
- @Override
public abstract void pause();
/**
* Tries to play next data source if applicable.
*/
// This is an asynchronous call.
- @Override
public abstract void skipToNext();
/**
@@ -584,7 +586,6 @@
* @param msec the offset in milliseconds from the start to seek to
*/
// This is an asynchronous call.
- @Override
public void seekTo(long msec) {
seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
}
@@ -594,7 +595,6 @@
*
* @return the current position in milliseconds
*/
- @Override
public abstract long getCurrentPosition();
/**
@@ -603,7 +603,6 @@
* @return the duration in milliseconds, if no duration is available
* (for example, if streaming live content), -1 is returned.
*/
- @Override
public abstract long getDuration();
/**
@@ -615,25 +614,60 @@
*
* @return the current buffered media source position in milliseconds
*/
- @Override
public abstract long getBufferedPosition();
/**
+ * MediaPlayer2 has not been prepared or just has been reset.
+ * In this state, MediaPlayer2 doesn't fetch data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_IDLE = 1001;
+
+ /**
+ * MediaPlayer2 has been just prepared.
+ * In this state, MediaPlayer2 just fetches data from media source,
+ * but doesn't actively render data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_PREPARED = 1002;
+
+ /**
+ * MediaPlayer2 is paused.
+ * In this state, MediaPlayer2 doesn't actively render data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_PAUSED = 1003;
+
+ /**
+ * MediaPlayer2 is actively playing back data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_PLAYING = 1004;
+
+ /**
+ * MediaPlayer2 has hit some fatal error and cannot continue playback.
+ * @hide
+ */
+ public static final int PLAYER_STATE_ERROR = 1005;
+
+ /**
+ * @hide
+ */
+ @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
+ PLAYER_STATE_IDLE,
+ PLAYER_STATE_PREPARED,
+ PLAYER_STATE_PAUSED,
+ PLAYER_STATE_PLAYING,
+ PLAYER_STATE_ERROR })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaPlayer2State {}
+
+ /**
* Gets the current player state.
*
* @return the current player state.
*/
- @Override
- public abstract @PlayerState int getPlayerState();
-
- /**
- * Gets the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- * @return the buffering state, one of the following:
- */
- @Override
- public abstract @BuffState int getBufferingState();
+ public abstract @MediaPlayer2State int getState();
/**
* Sets the audio attributes for this MediaPlayer2.
@@ -643,14 +677,12 @@
* @param attributes a non-null set of audio attributes
*/
// This is an asynchronous call.
- @Override
public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
/**
* Gets the audio attributes for this MediaPlayer2.
* @return attributes a set of audio attributes
*/
- @Override
public abstract @Nullable AudioAttributes getAudioAttributes();
/**
@@ -659,7 +691,6 @@
* @param dsd the descriptor of data source you want to play
*/
// This is an asynchronous call.
- @Override
public abstract void setDataSource(@NonNull DataSourceDesc dsd);
/**
@@ -669,7 +700,6 @@
* @param dsd the descriptor of data source you want to play after current one
*/
// This is an asynchronous call.
- @Override
public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
/**
@@ -678,7 +708,6 @@
* @param dsds the list of data sources you want to play after current one
*/
// This is an asynchronous call.
- @Override
public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
/**
@@ -686,7 +715,6 @@
*
* @return the current DataSourceDesc
*/
- @Override
public abstract @NonNull DataSourceDesc getCurrentDataSource();
/**
@@ -694,44 +722,9 @@
* @param loop true if the current data source is meant to loop.
*/
// This is an asynchronous call.
- @Override
public abstract void loopCurrent(boolean loop);
/**
- * Sets the playback speed.
- * A value of 1.0f is the default playback value.
- * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
- * before using negative values.<br>
- * After changing the playback speed, it is recommended to query the actual speed supported
- * by the player, see {@link #getPlaybackSpeed()}.
- * @param speed the desired playback speed
- */
- // This is an asynchronous call.
- @Override
- public abstract void setPlaybackSpeed(float speed);
-
- /**
- * Returns the actual playback speed to be used by the player when playing.
- * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
- * @return the actual playback speed
- */
- @Override
- public float getPlaybackSpeed() {
- return 1.0f;
- }
-
- /**
- * Indicates whether reverse playback is supported.
- * Reverse playback is indicated by negative playback speeds, see
- * {@link #setPlaybackSpeed(float)}.
- * @return true if reverse playback is supported.
- */
- @Override
- public boolean isReversePlaybackSupported() {
- return false;
- }
-
- /**
* Sets the volume of the audio of the media to play, expressed as a linear multiplier
* on the audio samples.
* Note that this volume is specific to the player, and is separate from stream volume
@@ -741,7 +734,6 @@
* @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
*/
// This is an asynchronous call.
- @Override
public abstract void setPlayerVolume(float volume);
/**
@@ -749,36 +741,16 @@
* Note that it does not take into account the associated stream volume.
* @return the player volume.
*/
- @Override
public abstract float getPlayerVolume();
/**
* @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
*/
- @Override
public float getMaxPlayerVolume() {
return 1.0f;
}
/**
- * Adds a callback to be notified of events for this player.
- * @param e the {@link Executor} to be used for the events.
- * @param cb the callback to receive the events.
- */
- // This is a synchronous call.
- @Override
- public abstract void registerPlayerEventCallback(@NonNull Executor e,
- @NonNull PlayerEventCallback cb);
-
- /**
- * Removes a previously registered callback for player events
- * @param cb the callback to remove
- */
- // This is a synchronous call.
- @Override
- public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
- /**
* Create a request parcel which can be routed to the native media
* player using {@link #invoke(Parcel, Parcel)}. The Parcel
* returned has the proper InterfaceToken set. The caller should
@@ -812,10 +784,10 @@
/**
* Insert a task in the command queue to help the client to identify whether a batch
* of commands has been finished. When this command is processed, a notification
- * {@code MediaPlayer2EventCallback.onCommandLabelReached} will be fired with the
+ * {@code EventCallback.onCommandLabelReached} will be fired with the
* given {@code label}.
*
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+ * @see android.media.MediaPlayer2.EventCallback#onCommandLabelReached
*
* @param label An application specific Object used to help to identify the completeness
* of a batch of commands.
@@ -973,7 +945,8 @@
*/
// This is a synchronous call.
@Override
- public abstract void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener);
+ public abstract void removeOnRoutingChangedListener(
+ AudioRouting.OnRoutingChangedListener listener);
/**
* Set the low-level power management behavior for this MediaPlayer2.
@@ -1010,9 +983,9 @@
*
* @return the width of the video, or 0 if there is no video,
* no display surface was set, or the width has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the width
* is available.
*/
public abstract int getVideoWidth();
@@ -1022,9 +995,10 @@
*
* @return the height of the video, or 0 if there is no video,
* no display surface was set, or the height has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height is available.
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the height is
+ * available.
*/
public abstract int getVideoHeight();
@@ -1051,60 +1025,6 @@
public abstract boolean isPlaying();
/**
- * MediaPlayer2 has not been prepared or just has been reset.
- * In this state, MediaPlayer2 doesn't fetch data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_IDLE = 1;
-
- /**
- * MediaPlayer2 has been just prepared.
- * In this state, MediaPlayer2 just fetches data from media source,
- * but doesn't actively render data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_PREPARED = 2;
-
- /**
- * MediaPlayer2 is paused.
- * In this state, MediaPlayer2 doesn't actively render data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_PAUSED = 3;
-
- /**
- * MediaPlayer2 is actively playing back data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_PLAYING = 4;
-
- /**
- * MediaPlayer2 has hit some fatal error and cannot continue playback.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_ERROR = 5;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
- MEDIAPLAYER2_STATE_IDLE,
- MEDIAPLAYER2_STATE_PREPARED,
- MEDIAPLAYER2_STATE_PAUSED,
- MEDIAPLAYER2_STATE_PLAYING,
- MEDIAPLAYER2_STATE_ERROR })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaPlayer2State {}
-
- /**
- * Gets the current MediaPlayer2 state.
- *
- * @return the current MediaPlayer2 state.
- * @hide
- */
- public abstract @MediaPlayer2State int getMediaPlayer2State();
-
- /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
* Each type of data source might have different set of default params.
@@ -1383,7 +1303,6 @@
* data source and calling prepare().
*/
// This is a synchronous call.
- @Override
public abstract void reset();
/**
@@ -1706,7 +1625,7 @@
* Interface definition for callbacks to be invoked when the player has the corresponding
* events.
*/
- public abstract static class MediaPlayer2EventCallback {
+ public abstract static class EventCallback {
/**
* Called to indicate the video size
*
@@ -1718,7 +1637,8 @@
* @param width the width of the video
* @param height the height of the video
*/
- public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
+ public void onVideoSizeChanged(
+ MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
/**
* Called to indicate an avaliable timed text
@@ -1814,14 +1734,14 @@
* @param executor the executor through which the callback should be invoked
*/
// This is a synchronous call.
- public abstract void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull MediaPlayer2EventCallback eventCallback);
+ public abstract void setEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull EventCallback eventCallback);
/**
- * Clears the {@link MediaPlayer2EventCallback}.
+ * Clears the {@link EventCallback}.
*/
// This is a synchronous call.
- public abstract void clearMediaPlayer2EventCallback();
+ public abstract void clearEventCallback();
/**
* Interface definition of a callback to be invoked when a
@@ -1849,14 +1769,14 @@
* in include/media/mediaplayer2.h!
*/
/** Unspecified media player error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+ * @see android.media.MediaPlayer2.EventCallback#onError
*/
public static final int MEDIA_ERROR_UNKNOWN = 1;
/** The video is streamed and its container is not valid for progressive
* playback i.e the video's index (e.g moov atom) is not at the start of the
* file.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+ * @see android.media.MediaPlayer2.EventCallback#onError
*/
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
@@ -1872,7 +1792,7 @@
/** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
* system/core/include/utils/Errors.h
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+ * @see android.media.MediaPlayer2.EventCallback#onError
* @hide
*/
public static final int MEDIA_ERROR_SYSTEM = -2147483648;
@@ -1896,62 +1816,62 @@
* in include/media/mediaplayer2.h!
*/
/** Unspecified media player info.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNKNOWN = 1;
/** The player switched to this datas source because it is the
* next-to-be-played in the playlist.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
/** The player just pushed the very first video frame for rendering.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
/** The player just rendered the very first audio sample.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4;
/** The player just completed the playback of this data source.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5;
/** The player just completed the playback of the full playlist.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_PLAYLIST_END = 6;
/** The player just prepared a data source.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_PREPARED = 100;
/** The video is too complex for the decoder: it can't decode frames fast
* enough. Possibly only the audio plays fine at this stage.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
/** MediaPlayer2 is temporarily pausing playback internally in order to
* buffer more data.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_START = 701;
/** MediaPlayer2 is resuming playback after filling buffers.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_END = 702;
/** Estimated network bandwidth information (kbps) is available; currently this event fires
* simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
* when playing network files.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
* @hide
*/
public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
@@ -1963,26 +1883,26 @@
* has already been played indicates that the next 30 percent of the
* content to play has been buffered.
*
- * The {@code extra} parameter in {@code MediaPlayer2EventCallback.onInfo} is the
+ * The {@code extra} parameter in {@code EventCallback.onInfo} is the
* percentage (0-100) of the content that has been buffered or played thus far.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_UPDATE = 704;
/** Bad interleaving means that a media has been improperly interleaved or
* not interleaved at all, e.g has all the video samples first then all the
* audio ones. Video is playing but a lot of disk seeks may be happening.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
/** The media cannot be seeked (e.g live stream)
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
/** A new set of metadata is available.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_METADATA_UPDATE = 802;
@@ -1994,30 +1914,30 @@
/** Informs that audio is not playing. Note that playback of the video
* is not interrupted.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
/** Informs that video is not playing. Note that playback of the audio
* is not interrupted.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
/** Failed to handle timed text track properly.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*
* {@hide}
*/
public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
/** Subtitle track was not supported by the media framework.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
/** Reading the subtitle track takes too long.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
@@ -2052,129 +1972,129 @@
//--------------------------------------------------------------------------
/** The player just completed a call {@link #attachAuxEffect}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1;
/** The player just completed a call {@link #deselectTrack}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_DESELECT_TRACK = 2;
/** The player just completed a call {@link #loopCurrent}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_LOOP_CURRENT = 3;
/** The player just completed a call {@link #pause}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_PAUSE = 4;
/** The player just completed a call {@link #play}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_PLAY = 5;
/** The player just completed a call {@link #prepare}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_PREPARE = 6;
/** The player just completed a call {@link #releaseDrm}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_RELEASE_DRM = 12;
/** The player just completed a call {@link #restoreDrmKeys}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13;
/** The player just completed a call {@link #seekTo}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SEEK_TO = 14;
/** The player just completed a call {@link #selectTrack}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SELECT_TRACK = 15;
/** The player just completed a call {@link #setAudioAttributes}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16;
/** The player just completed a call {@link #setAudioSessionId}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17;
/** The player just completed a call {@link #setAuxEffectSendLevel}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18;
/** The player just completed a call {@link #setDataSource}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19;
/** The player just completed a call {@link #setNextDataSource}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22;
/** The player just completed a call {@link #setNextDataSources}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23;
/** The player just completed a call {@link #setPlaybackParams}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24;
/** The player just completed a call {@link #setPlaybackSpeed}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25;
/** The player just completed a call {@link #setPlayerVolume}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26;
/** The player just completed a call {@link #setSurface}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_SURFACE = 27;
/** The player just completed a call {@link #setSyncParams}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28;
/** The player just completed a call {@link #skipToNext}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29;
/** The player just completed a call {@link #setBufferingParams}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
* @hide
*/
public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
/** The player just completed a call {@code setVideoScalingMode}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
* @hide
*/
public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
/** The player just completed a call {@code notifyWhenCommandLabelReached}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+ * @see android.media.MediaPlayer2.EventCallback#onCommandLabelReached
* @hide
*/
public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
@@ -2200,7 +2120,6 @@
CALL_COMPLETED_SET_NEXT_DATA_SOURCE,
CALL_COMPLETED_SET_NEXT_DATA_SOURCES,
CALL_COMPLETED_SET_PLAYBACK_PARAMS,
- CALL_COMPLETED_SET_PLAYBACK_SPEED,
CALL_COMPLETED_SET_PLAYER_VOLUME,
CALL_COMPLETED_SET_SURFACE,
CALL_COMPLETED_SET_SYNC_PARAMS,
@@ -2213,38 +2132,38 @@
public @interface CallCompleted {}
/** Status code represents that call is completed without an error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_NO_ERROR = 0;
/** Status code represents that call is ended with an unknown error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_ERROR_UNKNOWN = Integer.MIN_VALUE;
/** Status code represents that the player is not in valid state for the operation.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_INVALID_OPERATION = 1;
/** Status code represents that the argument is illegal.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_BAD_VALUE = 2;
/** Status code represents that the operation is not allowed.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_PERMISSION_DENIED = 3;
/** Status code represents a file or network related operation error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_ERROR_IO = 4;
/** Status code represents that DRM operation is called before preparing a DRM scheme through
* {@link #prepareDrm}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_NO_DRM_SCHEME = 5;
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 56423fd..2b61b2e 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -173,6 +173,11 @@
native_setup(new WeakReference<MediaPlayer2Impl>(this));
}
+ @Override
+ public MediaPlayerBase getMediaPlayerBase() {
+ return null;
+ }
+
/**
* Releases the resources held by this {@code MediaPlayer2} object.
*
@@ -313,39 +318,11 @@
}
@Override
- public @PlayerState int getPlayerState() {
- int mediaplayer2State = getMediaPlayer2State();
- int playerState;
- switch (mediaplayer2State) {
- case MEDIAPLAYER2_STATE_IDLE:
- playerState = PLAYER_STATE_IDLE;
- break;
- case MEDIAPLAYER2_STATE_PREPARED:
- case MEDIAPLAYER2_STATE_PAUSED:
- playerState = PLAYER_STATE_PAUSED;
- break;
- case MEDIAPLAYER2_STATE_PLAYING:
- playerState = PLAYER_STATE_PLAYING;
- break;
- case MEDIAPLAYER2_STATE_ERROR:
- default:
- playerState = PLAYER_STATE_ERROR;
- break;
- }
-
- return playerState;
+ public @MediaPlayer2State int getState() {
+ return native_getState();
}
- /**
- * Gets the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- */
- @Override
- public @BuffState int getBufferingState() {
- // TODO: use cached state or call native function.
- return BUFFERING_STATE_UNKNOWN;
- }
+ private native int native_getState();
/**
* Sets the audio attributes for this MediaPlayer2.
@@ -427,8 +404,8 @@
mNextSourceState = NEXT_SOURCE_STATE_INIT;
mNextSourcePlayPending = false;
}
- int state = getMediaPlayer2State();
- if (state != MEDIAPLAYER2_STATE_IDLE) {
+ int state = getState();
+ if (state != PLAYER_STATE_IDLE) {
synchronized (mSrcLock) {
prepareNextDataSource_l();
}
@@ -465,8 +442,8 @@
mNextSourceState = NEXT_SOURCE_STATE_INIT;
mNextSourcePlayPending = false;
}
- int state = getMediaPlayer2State();
- if (state != MEDIAPLAYER2_STATE_IDLE) {
+ int state = getState();
+ if (state != PLAYER_STATE_IDLE) {
synchronized (mSrcLock) {
prepareNextDataSource_l();
}
@@ -500,46 +477,6 @@
private native void setLooping(boolean looping);
/**
- * Sets the playback speed.
- * A value of 1.0f is the default playback value.
- * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
- * before using negative values.<br>
- * After changing the playback speed, it is recommended to query the actual speed supported
- * by the player, see {@link #getPlaybackSpeed()}.
- * @param speed the desired playback speed
- */
- @Override
- public void setPlaybackSpeed(float speed) {
- addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_SPEED, false) {
- @Override
- void process() {
- _setPlaybackParams(getPlaybackParams().setSpeed(speed));
- }
- });
- }
-
- /**
- * Returns the actual playback speed to be used by the player when playing.
- * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
- * @return the actual playback speed
- */
- @Override
- public float getPlaybackSpeed() {
- return getPlaybackParams().getSpeed();
- }
-
- /**
- * Indicates whether reverse playback is supported.
- * Reverse playback is indicated by negative playback speeds, see
- * {@link #setPlaybackSpeed(float)}.
- * @return true if reverse playback is supported.
- */
- @Override
- public boolean isReversePlaybackSupported() {
- return false;
- }
-
- /**
* Sets the volume of the audio of the media to play, expressed as a linear multiplier
* on the audio samples.
* Note that this volume is specific to the player, and is separate from stream volume
@@ -579,25 +516,6 @@
return 1.0f;
}
- /**
- * Adds a callback to be notified of events for this player.
- * @param e the {@link Executor} to be used for the events.
- * @param cb the callback to receive the events.
- */
- @Override
- public void registerPlayerEventCallback(@NonNull Executor e,
- @NonNull PlayerEventCallback cb) {
- }
-
- /**
- * Removes a previously registered callback for player events
- * @param cb the callback to remove
- */
- @Override
- public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb) {
- }
-
-
private static final int NEXT_SOURCE_STATE_ERROR = -1;
private static final int NEXT_SOURCE_STATE_INIT = 0;
private static final int NEXT_SOURCE_STATE_PREPARING = 1;
@@ -666,7 +584,7 @@
@Override
void process() {
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onCommandLabelReached(
MediaPlayer2Impl.this, label));
}
@@ -1305,9 +1223,9 @@
*
* @return the width of the video, or 0 if there is no video,
* no display surface was set, or the width has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the width
* is available.
*/
@Override
@@ -1318,9 +1236,9 @@
*
* @return the height of the video, or 0 if there is no video,
* no display surface was set, or the height has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the height
* is available.
*/
@Override
@@ -1355,13 +1273,6 @@
@Override
public native boolean isPlaying();
- @Override
- public @MediaPlayer2State int getMediaPlayer2State() {
- return native_getMediaPlayer2State();
- }
-
- private native int native_getMediaPlayer2State();
-
/**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
@@ -2820,7 +2731,7 @@
if (dsd != null) {
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
}
@@ -2882,7 +2793,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
@@ -2916,7 +2827,7 @@
synchronized (mEventCbLock) {
if (srcId == mCurrentSrcId) {
mBufferedPercentageCurrent.set(percent);
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, mCurrentDSD, MEDIA_INFO_BUFFERING_UPDATE,
percent));
@@ -2924,7 +2835,7 @@
} else if (srcId == mNextSrcId && !mNextDSDs.isEmpty()) {
mBufferedPercentageNext.set(percent);
DataSourceDesc nextDSD = mNextDSDs.get(0);
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, nextDSD, MEDIA_INFO_BUFFERING_UPDATE,
percent));
@@ -2962,7 +2873,7 @@
final int width = msg.arg1;
final int height = msg.arg2;
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onVideoSizeChanged(
mMediaPlayer, mCurrentDSD, width, height));
}
@@ -2974,7 +2885,7 @@
{
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onError(
mMediaPlayer, mCurrentDSD, what, extra));
cb.first.execute(() -> cb.second.onInfo(
@@ -3027,7 +2938,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, mCurrentDSD, what, extra));
}
@@ -3057,7 +2968,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, mCurrentDSD, text));
}
}
@@ -3091,7 +3002,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
mMediaPlayer, mCurrentDSD, data));
}
@@ -3196,8 +3107,8 @@
}
private final Object mEventCbLock = new Object();
- private ArrayList<Pair<Executor, MediaPlayer2EventCallback> > mEventCallbackRecords
- = new ArrayList<Pair<Executor, MediaPlayer2EventCallback> >();
+ private ArrayList<Pair<Executor, EventCallback> > mEventCallbackRecords
+ = new ArrayList<Pair<Executor, EventCallback> >();
/**
* Register a callback to be invoked when the media source is ready
@@ -3207,14 +3118,14 @@
* @param executor the executor through which the callback should be invoked
*/
@Override
- public void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull MediaPlayer2EventCallback eventCallback) {
+ public void setEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull EventCallback eventCallback) {
if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
+ throw new IllegalArgumentException("Illegal null EventCallback");
}
if (executor == null) {
throw new IllegalArgumentException(
- "Illegal null Executor for the MediaPlayer2EventCallback");
+ "Illegal null Executor for the EventCallback");
}
synchronized (mEventCbLock) {
mEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -3222,10 +3133,10 @@
}
/**
- * Clears the {@link MediaPlayer2EventCallback}.
+ * Clears the {@link EventCallback}.
*/
@Override
- public void clearMediaPlayer2EventCallback() {
+ public void clearEventCallback() {
synchronized (mEventCbLock) {
mEventCallbackRecords.clear();
}
@@ -3281,11 +3192,11 @@
public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull DrmEventCallback eventCallback) {
if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
+ throw new IllegalArgumentException("Illegal null EventCallback");
}
if (executor == null) {
throw new IllegalArgumentException(
- "Illegal null Executor for the MediaPlayer2EventCallback");
+ "Illegal null Executor for the EventCallback");
}
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -4786,7 +4697,7 @@
return;
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onCallCompleted(
MediaPlayer2Impl.this, mDSD, mMediaCallType, status));
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 70ab863..d5afd9a 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -610,7 +610,9 @@
&& mSelectedRoute != mBluetoothA2dpRoute)) {
return;
}
- Log.v(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
+ if (DEBUG) {
+ Log.d(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
+ }
mSelectedRoute.select();
}
});
@@ -965,19 +967,6 @@
&& (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
try {
sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
- // TODO: Remove the following logging when no longer needed.
- if (route != btRoute) {
- StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- StringBuffer sb = new StringBuffer();
- // callStack[3] is the caller of this method.
- for (int i = 3; i < callStack.length; i++) {
- StackTraceElement caller = callStack[i];
- sb.append(caller.getClassName() + "." + caller.getMethodName()
- + ":" + caller.getLineNumber()).append(" ");
- }
- Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
- + sStatic.mPackageName + ", callers=" + sb.toString());
- }
} catch (RemoteException e) {
Log.e(TAG, "Error changing Bluetooth A2DP state", e);
}
@@ -1064,7 +1053,9 @@
}
static void addRouteStatic(RouteInfo info) {
- Log.v(TAG, "Adding route: " + info);
+ if (DEBUG) {
+ Log.d(TAG, "Adding route: " + info);
+ }
final RouteCategory cat = info.getCategory();
if (!sStatic.mCategories.contains(cat)) {
sStatic.mCategories.add(cat);
@@ -1119,7 +1110,9 @@
}
static void removeRouteStatic(RouteInfo info) {
- Log.v(TAG, "Removing route: " + info);
+ if (DEBUG) {
+ Log.d(TAG, "Removing route: " + info);
+ }
if (sStatic.mRoutes.remove(info)) {
final RouteCategory removingCat = info.getCategory();
final int count = sStatic.mRoutes.size();
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 6546cf0..d166cc3 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -786,13 +786,13 @@
}
static jint
-android_media_MediaPlayer2_getMediaPlayer2State(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getState(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
return MEDIAPLAYER2_STATE_IDLE;
}
- return (jint)mp->getMediaPlayer2State();
+ return (jint)mp->getState();
}
static jint
@@ -1502,7 +1502,7 @@
{"_prepare", "()V", (void *)android_media_MediaPlayer2_prepare},
{"_start", "()V", (void *)android_media_MediaPlayer2_start},
{"_stop", "()V", (void *)android_media_MediaPlayer2_stop},
- {"native_getMediaPlayer2State", "()I", (void *)android_media_MediaPlayer2_getMediaPlayer2State},
+ {"native_getState", "()I", (void *)android_media_MediaPlayer2_getState},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer2_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer2_getVideoHeight},
{"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/midi.h
index 780d8a7..755d09f 100644
--- a/media/native/midi/include/midi.h
+++ b/media/native/midi/include/midi.h
@@ -71,7 +71,7 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred.
*/
media_status_t AMIDI_API AMidiDevice_fromJava(
- JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr);
+ JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29);
/**
* Disconnects the native Midi Device Object from the associated Java MidiDevice object.
@@ -88,7 +88,7 @@
* - the JNI interface initialization to the associated java MidiDevice failed.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
*/
-media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice);
+media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
/**
* Gets the MIDI device type.
@@ -104,7 +104,7 @@
* parameter is NULL.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error.
*/
-int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device);
+int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
/**
* Gets the number of input (sending) ports available on the specified MIDI device.
@@ -117,7 +117,7 @@
* parameter is NULL.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
*/
-ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device);
+ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
/**
* Gets the number of output (receiving) ports available on the specified MIDI device.
@@ -130,7 +130,7 @@
* parameter is NULL.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info.
*/
-ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device);
+ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
/*
* API for receiving data from the Output port of a device.
@@ -149,14 +149,14 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
*/
media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
- AMidiOutputPort **outOutputPortPtr);
+ AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29);
/**
* Closes the output port.
*
* @param outputPort The native API port identifier of the port.
*/
-void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort);
+void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29);
/**
* Receives the next pending MIDI message. To retrieve all pending messages, the client should
@@ -177,7 +177,7 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
*/
ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
- uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr);
+ uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29);
/*
* API for sending data to the Input port of a device.
@@ -196,7 +196,7 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
*/
media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
- AMidiInputPort **outInputPortPtr);
+ AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29);
/**
* Sends data to the specified input port.
@@ -210,7 +210,7 @@
* was NULL, the specified buffer was NULL.
*/
ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
- size_t numBytes);
+ size_t numBytes) __INTRODUCED_IN(29);
/**
* Sends data to the specified input port with a timestamp.
@@ -225,7 +225,7 @@
* was NULL, the specified buffer was NULL.
*/
ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
- const uint8_t *buffer, size_t numBytes, int64_t timestamp);
+ const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29);
/**
* Sends a message with a 'MIDI flush command code' to the specified port. This should cause
@@ -239,14 +239,14 @@
* @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't
* be sent.
*/
-media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort);
+media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
/**
* Closes the input port.
*
* @param inputPort Identifies the input (sending) port to close.
*/
-void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort);
+void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
#ifdef __cplusplus
}
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 72647ab..617e49a 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -18,7 +18,6 @@
<string name="app_name">Android Services Library</string>
<string name="notification_assistant">Notification Assistant</string>
- <string name="prompt_block_reason">Too many dismissals:views</string>
<string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
<string-array name="autofill_field_classification_available_algorithms">
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f878822..fdd6a9c 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -292,8 +292,7 @@
if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- return new Adjustment(packageName, key, signals,
- getContext().getString(R.string.prompt_block_reason), user);
+ return new Adjustment(packageName, key, signals, "", user);
}
// for testing
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index ab03744..96012c1 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -26,6 +26,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MIN_SDK_VERSION := 21
+
include $(BUILD_STATIC_JAVA_LIBRARY)
# For the test package.
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 8473c06..a18600a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -40,9 +40,6 @@
public class AccessibilityUtils {
public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
- final static TextUtils.SimpleStringSplitter sStringColonSplitter =
- new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
-
/**
* @return the set of enabled accessibility services. If there are no services,
* it returns the unmodifiable {@link Collections#emptySet()}.
@@ -72,16 +69,16 @@
final String enabledServicesSetting = Settings.Secure.getStringForUser(
context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userId);
- if (enabledServicesSetting == null) {
+ if (TextUtils.isEmpty(enabledServicesSetting)) {
return Collections.emptySet();
}
final Set<ComponentName> enabledServices = new HashSet<>();
- final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter;
+ final TextUtils.StringSplitter colonSplitter =
+ new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
colonSplitter.setString(enabledServicesSetting);
- while (colonSplitter.hasNext()) {
- final String componentNameString = colonSplitter.next();
+ for (String componentNameString : colonSplitter) {
final ComponentName enabledService = ComponentName.unflattenFromString(
componentNameString);
if (enabledService != null) {
@@ -168,8 +165,7 @@
* an OEM-configurable default if the setting has never been set.
*
* @param context A valid context
- * @param userId The user whose settings should be checked
- *
+ * @param userId The user whose settings should be checked
* @return The component name, flattened to a string, of the target service.
*/
public static String getShortcutTargetServiceComponentNameString(
@@ -187,9 +183,9 @@
* Check if the accessibility shortcut is enabled for a user
*
* @param context A valid context
- * @param userId The user of interest
+ * @param userId The user of interest
* @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise.
- * Note that the shortcut may be enabled, but no action associated with it.
+ * Note that the shortcut may be enabled, but no action associated with it.
*/
public static boolean isShortcutEnabled(Context context, int userId) {
return Settings.Secure.getIntForUser(context.getContentResolver(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index f1d43bf..b55d2ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -16,6 +16,10 @@
package com.android.settingslib.drawer;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -169,4 +173,11 @@
return new Tile[size];
}
};
+
+ public boolean isPrimaryProfileOnly() {
+ String profile = metaData != null ?
+ metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ profile = (profile != null ? profile : PROFILE_ALL);
+ return TextUtils.equals(profile, PROFILE_PRIMARY);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 0f0e4e5..96ed0cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -34,8 +34,8 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.widget.RemoteViews;
+import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -96,11 +96,7 @@
/**
* The key used to get the category from metadata of activities of action
* {@link #EXTRA_SETTINGS_ACTION}
- * The value must be one of:
- * <li>com.android.settings.category.wireless</li>
- * <li>com.android.settings.category.device</li>
- * <li>com.android.settings.category.personal</li>
- * <li>com.android.settings.category.system</li>
+ * The value must be from {@link CategoryKey}.
*/
private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
@@ -171,20 +167,37 @@
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
"com.android.settings.summary_uri";
- /**
- * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
- * custom view which should be displayed for the preference. The custom view will be inflated
- * as a remote view.
- *
- * This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
- * of the summary TextView to '@android:id/summary'.
- */
- public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
- "com.android.settings.custom_view";
-
public static final String SETTING_PKG = "com.android.settings";
/**
+ * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
+ * the app will always be run in the primary profile.
+ *
+ * @see #META_DATA_KEY_PROFILE
+ */
+ public static final String PROFILE_PRIMARY = "primary_profile_only";
+
+ /**
+ * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the user
+ * will be presented with a dialog to choose the profile the app will be run in.
+ *
+ * @see #META_DATA_KEY_PROFILE
+ */
+ public static final String PROFILE_ALL = "all_profiles";
+
+ /**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml
+ * to specify the profile in which the app should be run when the device has a managed profile.
+ * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
+ * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
+ * run in the primary profile.
+ *
+ * @see #PROFILE_PRIMARY
+ * @see #PROFILE_ALL
+ */
+ public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
+
+ /**
* Build a list of DashboardCategory. Each category must be defined in manifest.
* eg: .Settings$DeviceSettings
* @deprecated
@@ -442,11 +455,6 @@
keyHint = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
}
}
- if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
- int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
- tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
- updateSummaryAndTitle(context, providerMap, tile);
- }
}
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index f7aa297..87f5b4f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -41,6 +41,7 @@
import android.util.Pair;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.time.ZonedDateTime;
import java.util.Date;
@@ -86,7 +87,8 @@
* mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
}
- private INetworkStatsSession getSession() {
+ @VisibleForTesting
+ INetworkStatsSession getSession() {
if (mSession == null) {
try {
mSession = mStatsService.openSession();
@@ -176,6 +178,30 @@
}
}
+ /**
+ * Get the total usage level recorded in the network history
+ * @param template the network template to retrieve the network history
+ * @return the total usage level recorded in the network history
+ */
+ public long getHistoriclUsageLevel(NetworkTemplate template) {
+ final INetworkStatsSession session = getSession();
+ if (session != null) {
+ try {
+ final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
+ final long now = System.currentTimeMillis();
+ final NetworkStatsHistory.Entry entry =
+ history.getValues(0L /* start */, now /* end */, now, null /* recycle */);
+ if (entry != null) {
+ return entry.rxBytes + entry.txBytes;
+ }
+ Log.w(TAG, "Failed to get data usage, no entry data");
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get data usage, remote call failed");
+ }
+ }
+ return 0L;
+ }
+
private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
if (mPolicyManager == null || template == null) return null;
final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
deleted file mode 100644
index a890920..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.suggestions;
-
-import android.content.Intent;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.settingslib.drawer.Tile;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class SuggestionList {
- // Category -> list of suggestion map
- private final Map<SuggestionCategory, List<Tile>> mSuggestions;
-
- // A flatten list of all suggestions.
- private List<Tile> mSuggestionList;
-
- public SuggestionList() {
- mSuggestions = new ArrayMap<>();
- }
-
- public void addSuggestions(SuggestionCategory category, List<Tile> suggestions) {
- mSuggestions.put(category, suggestions);
- }
-
- public List<Tile> getSuggestions() {
- if (mSuggestionList != null) {
- return mSuggestionList;
- }
- mSuggestionList = new ArrayList<>();
- for (List<Tile> suggestions : mSuggestions.values()) {
- mSuggestionList.addAll(suggestions);
- }
- dedupeSuggestions(mSuggestionList);
- return mSuggestionList;
- }
-
- public boolean isExclusiveSuggestionCategory() {
- if (mSuggestions.size() != 1) {
- // If there is no category, or more than 1 category, it's not exclusive by definition.
- return false;
- }
- for (SuggestionCategory category : mSuggestions.keySet()) {
- if (category.exclusive) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Filter suggestions list so they are all unique.
- */
- private void dedupeSuggestions(List<Tile> suggestions) {
- final Set<String> intents = new ArraySet<>();
- for (int i = suggestions.size() - 1; i >= 0; i--) {
- final Tile suggestion = suggestions.get(i);
- final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
- if (intents.contains(intentUri)) {
- suggestions.remove(i);
- } else {
- intents.add(intentUri);
- }
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
deleted file mode 100644
index 8705c98..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.suggestions;
-
-import android.Manifest;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import androidx.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Xml;
-import android.view.InflateException;
-
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class SuggestionParser {
-
- private static final String TAG = "SuggestionParser";
-
- // If defined, only returns this suggestion if the feature is supported.
- public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
-
- // If defined, only display this optional step if an account of that type exists.
- private static final String META_DATA_REQUIRE_ACCOUNT = "com.android.settings.require_account";
-
- // If defined and not true, do not should optional step.
- private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported";
-
- // If defined, only display this optional step if the current user is of that type.
- private static final String META_DATA_REQUIRE_USER_TYPE =
- "com.android.settings.require_user_type";
-
- // If defined, only display this optional step if a connection is available.
- private static final String META_DATA_IS_CONNECTION_REQUIRED =
- "com.android.settings.require_connection";
-
- // The valid values that setup wizard recognizes for differentiating user types.
- private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary";
- private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin";
- private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest";
- private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted";
-
- /**
- * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
- * For instance:
- * 0,10
- * Will appear immediately, but if the user removes it, it will come back after 10 days.
- *
- * Another example:
- * 10,30
- * Will only show up after 10 days, and then again after 30.
- */
- public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
-
- // Shared prefs keys for storing dismissed state.
- // Index into current dismissed state.
- public static final String SETUP_TIME = "_setup_time";
- private static final String IS_DISMISSED = "_is_dismissed";
-
- // Default dismiss control for smart suggestions.
- private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
-
- private final Context mContext;
- private final List<SuggestionCategory> mSuggestionList;
- private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
- private final SharedPreferences mSharedPrefs;
- private final String mDefaultDismissControl;
-
- public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
- String defaultDismissControl) {
- this(
- context,
- sharedPrefs,
- (List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
- defaultDismissControl);
- }
-
- public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
- this(context, sharedPrefs, orderXml, DEFAULT_SMART_DISMISS_CONTROL);
- }
-
- @VisibleForTesting
- public SuggestionParser(
- Context context,
- SharedPreferences sharedPrefs,
- List<SuggestionCategory> suggestionList,
- String defaultDismissControl) {
- mContext = context;
- mSuggestionList = suggestionList;
- mSharedPrefs = sharedPrefs;
- mDefaultDismissControl = defaultDismissControl;
- }
-
- public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
- final SuggestionList suggestionList = new SuggestionList();
- final int N = mSuggestionList.size();
- for (int i = 0; i < N; i++) {
- final SuggestionCategory category = mSuggestionList.get(i);
- if (category.exclusive && !isExclusiveCategoryExpired(category)) {
- // If suggestions from an exclusive category are present, parsing is stopped
- // and only suggestions from that category are displayed. Note that subsequent
- // exclusive categories are also ignored.
- final List<Tile> exclusiveSuggestions = new ArrayList<>();
-
- // Read suggestion and force isSmartSuggestion to be false so the rule defined
- // from each suggestion itself is used.
- readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
- if (!exclusiveSuggestions.isEmpty()) {
- final SuggestionList exclusiveList = new SuggestionList();
- exclusiveList.addSuggestions(category, exclusiveSuggestions);
- return exclusiveList;
- }
- } else {
- // Either the category is not exclusive, or the exclusiveness expired so we should
- // treat it as a normal category.
- final List<Tile> suggestions = new ArrayList<>();
- readSuggestions(category, suggestions, isSmartSuggestionEnabled);
- suggestionList.addSuggestions(category, suggestions);
- }
- }
- return suggestionList;
- }
-
- /**
- * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
- * be disabled.
- */
- public boolean dismissSuggestion(Tile suggestion) {
- final String keyBase = suggestion.intent.getComponent().flattenToShortString();
- mSharedPrefs.edit()
- .putBoolean(keyBase + IS_DISMISSED, true)
- .commit();
- return true;
- }
-
- @VisibleForTesting
- public void filterSuggestions(
- List<Tile> suggestions, int countBefore, boolean isSmartSuggestionEnabled) {
- for (int i = countBefore; i < suggestions.size(); i++) {
- if (!isAvailable(suggestions.get(i)) ||
- !isSupported(suggestions.get(i)) ||
- !satisifesRequiredUserType(suggestions.get(i)) ||
- !satisfiesRequiredAccount(suggestions.get(i)) ||
- !satisfiesConnectivity(suggestions.get(i)) ||
- isDismissed(suggestions.get(i), isSmartSuggestionEnabled)) {
- suggestions.remove(i--);
- }
- }
- }
-
- @VisibleForTesting
- void readSuggestions(
- SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
- int countBefore = suggestions.size();
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(category.category);
- if (category.pkg != null) {
- intent.setPackage(category.pkg);
- }
- TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
- mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */);
- filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
- if (!category.multiple && suggestions.size() > (countBefore + 1)) {
- // If there are too many, remove them all and only re-add the one with the highest
- // priority.
- Tile item = suggestions.remove(suggestions.size() - 1);
- while (suggestions.size() > countBefore) {
- Tile last = suggestions.remove(suggestions.size() - 1);
- if (last.priority > item.priority) {
- item = last;
- }
- }
- // If category is marked as done, do not add any item.
- if (!isCategoryDone(category.category)) {
- suggestions.add(item);
- }
- }
- }
-
- private boolean isAvailable(Tile suggestion) {
- final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
- if (featuresRequired != null) {
- for (String feature : featuresRequired.split(",")) {
- if (TextUtils.isEmpty(feature)) {
- Log.w(TAG, "Found empty substring when parsing required features: "
- + featuresRequired);
- } else if (!mContext.getPackageManager().hasSystemFeature(feature)) {
- Log.i(TAG, suggestion.title + " requires unavailable feature " + feature);
- return false;
- }
- }
- }
- return true;
- }
-
- @RequiresPermission(Manifest.permission.MANAGE_USERS)
- private boolean satisifesRequiredUserType(Tile suggestion) {
- final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE);
- if (requiredUser != null) {
- final UserManager userManager = mContext.getSystemService(UserManager.class);
- UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
- for (String userType : requiredUser.split("\\|")) {
- final boolean primaryUserCondtionMet = userInfo.isPrimary()
- && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType);
- final boolean adminUserConditionMet = userInfo.isAdmin()
- && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType);
- final boolean guestUserCondtionMet = userInfo.isGuest()
- && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType);
- final boolean restrictedUserCondtionMet = userInfo.isRestricted()
- && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType);
- if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet
- || restrictedUserCondtionMet) {
- return true;
- }
- }
- Log.i(TAG, suggestion.title + " requires user type " + requiredUser);
- return false;
- }
- return true;
- }
-
- public boolean satisfiesRequiredAccount(Tile suggestion) {
- final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
- if (requiredAccountType == null) {
- return true;
- }
- AccountManager accountManager = mContext.getSystemService(AccountManager.class);
- Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
- boolean satisfiesRequiredAccount = accounts.length > 0;
- if (!satisfiesRequiredAccount) {
- Log.i(TAG, suggestion.title + " requires unavailable account type "
- + requiredAccountType);
- }
- return satisfiesRequiredAccount;
- }
-
- public boolean isSupported(Tile suggestion) {
- final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED);
- try {
- if (suggestion.intent == null) {
- return false;
- }
- final Resources res = mContext.getPackageManager().getResourcesForActivity(
- suggestion.intent.getComponent());
- boolean isSupported =
- isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true;
- if (!isSupported) {
- Log.i(TAG, suggestion.title + " requires unsupported resource "
- + isSupportedResource);
- }
- return isSupported;
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent());
- return false;
- } catch (Resources.NotFoundException e) {
- Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent(), e);
- return false;
- }
- }
-
- private boolean satisfiesConnectivity(Tile suggestion) {
- final boolean isConnectionRequired =
- suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED);
- if (!isConnectionRequired) {
- return true;
- }
- ConnectivityManager cm =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo netInfo = cm.getActiveNetworkInfo();
- boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting();
- if (!satisfiesConnectivity) {
- Log.i(TAG, suggestion.title + " is missing required connection.");
- }
- return satisfiesConnectivity;
- }
-
- public boolean isCategoryDone(String category) {
- String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
- return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
- }
-
- public void markCategoryDone(String category) {
- String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
- Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
- }
-
- /**
- * Whether or not the category's exclusiveness has expired.
- */
- private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
- final String keySetupTime = category.category + SETUP_TIME;
- final long currentTime = System.currentTimeMillis();
- if (!mSharedPrefs.contains(keySetupTime)) {
- mSharedPrefs.edit()
- .putLong(keySetupTime, currentTime)
- .commit();
- }
- if (category.exclusiveExpireDaysInMillis < 0) {
- // negative means never expires
- return false;
- }
- final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
- final long elapsedTime = currentTime - setupTime;
- Log.d(TAG, "Day " + elapsedTime / DateUtils.DAY_IN_MILLIS + " for " + category.category);
- return elapsedTime > category.exclusiveExpireDaysInMillis;
- }
-
- @VisibleForTesting
- boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
- String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
- String keyBase = suggestion.intent.getComponent().flattenToShortString();
- if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
- mSharedPrefs.edit()
- .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
- .commit();
- }
- // Check if it's already manually dismissed
- final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
- if (isDismissed) {
- return true;
- }
- if (dismissControl == null) {
- return false;
- }
- // Parse when suggestion should first appear. return true to artificially hide suggestion
- // before then.
- int firstAppearDay = parseDismissString(dismissControl);
- long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
- firstAppearDay);
- if (System.currentTimeMillis() >= firstAppearDayInMs) {
- // Dismiss timeout has passed, undismiss it.
- mSharedPrefs.edit()
- .putBoolean(keyBase + IS_DISMISSED, false)
- .commit();
- return false;
- }
- return true;
- }
-
- private long getEndTime(long startTime, int daysDelay) {
- long days = daysDelay * DateUtils.DAY_IN_MILLIS;
- return startTime + days;
- }
-
- /**
- * Parse the first int from a string formatted as "0,1,2..."
- * The value means suggestion should first appear on Day X.
- */
- private int parseDismissString(String dismissControl) {
- final String[] dismissStrs = dismissControl.split(",");
- return Integer.parseInt(dismissStrs[0]);
- }
-
- private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
- if (isSmartSuggestionEnabled) {
- return mDefaultDismissControl;
- } else {
- return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
- }
- }
-
- private static class SuggestionOrderInflater {
- private static final String TAG_LIST = "optional-steps";
- private static final String TAG_ITEM = "step";
-
- private static final String ATTR_CATEGORY = "category";
- private static final String ATTR_PACKAGE = "package";
- private static final String ATTR_MULTIPLE = "multiple";
- private static final String ATTR_EXCLUSIVE = "exclusive";
- private static final String ATTR_EXCLUSIVE_EXPIRE_DAYS = "exclusiveExpireDays";
-
- private final Context mContext;
-
- public SuggestionOrderInflater(Context context) {
- mContext = context;
- }
-
- public Object parse(int resource) {
- XmlPullParser parser = mContext.getResources().getXml(resource);
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- try {
- // Look for the root node.
- int type;
- do {
- type = parser.next();
- } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
-
- if (type != XmlPullParser.START_TAG) {
- throw new InflateException(parser.getPositionDescription()
- + ": No start tag found!");
- }
-
- // Temp is the root that was found in the xml
- Object xmlRoot = onCreateItem(parser.getName(), attrs);
-
- // Inflate all children under temp
- rParse(parser, xmlRoot, attrs);
- return xmlRoot;
- } catch (XmlPullParserException | IOException e) {
- Log.w(TAG, "Problem parser resource " + resource, e);
- return null;
- }
- }
-
- /**
- * Recursive method used to descend down the xml hierarchy and instantiate
- * items, instantiate their children.
- */
- private void rParse(XmlPullParser parser, Object parent, final AttributeSet attrs)
- throws XmlPullParserException, IOException {
- final int depth = parser.getDepth();
-
- int type;
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- final String name = parser.getName();
-
- Object item = onCreateItem(name, attrs);
- onAddChildItem(parent, item);
- rParse(parser, item, attrs);
- }
- }
-
- protected void onAddChildItem(Object parent, Object child) {
- if (parent instanceof List<?> && child instanceof SuggestionCategory) {
- ((List<SuggestionCategory>) parent).add((SuggestionCategory) child);
- } else {
- throw new IllegalArgumentException("Parent was not a list");
- }
- }
-
- protected Object onCreateItem(String name, AttributeSet attrs) {
- if (name.equals(TAG_LIST)) {
- return new ArrayList<SuggestionCategory>();
- } else if (name.equals(TAG_ITEM)) {
- SuggestionCategory category = new SuggestionCategory();
- category.category = attrs.getAttributeValue(null, ATTR_CATEGORY);
- category.pkg = attrs.getAttributeValue(null, ATTR_PACKAGE);
- String multiple = attrs.getAttributeValue(null, ATTR_MULTIPLE);
- category.multiple = !TextUtils.isEmpty(multiple) && Boolean.parseBoolean(multiple);
- String exclusive = attrs.getAttributeValue(null, ATTR_EXCLUSIVE);
- category.exclusive =
- !TextUtils.isEmpty(exclusive) && Boolean.parseBoolean(exclusive);
- String expireDaysAttr = attrs.getAttributeValue(null,
- ATTR_EXCLUSIVE_EXPIRE_DAYS);
- long expireDays = !TextUtils.isEmpty(expireDaysAttr)
- ? Integer.parseInt(expireDaysAttr)
- : -1;
- category.exclusiveExpireDaysInMillis = DateUtils.DAY_IN_MILLIS * expireDays;
- return category;
- } else {
- throw new IllegalArgumentException("Unknown item " + name);
- }
- }
- }
-}
-
diff --git a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml b/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
deleted file mode 100644
index f02ac15..0000000
--- a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<optional-steps>
- <step category="com.android.settings.suggested.category.DEFERRED_SETUP"
- exclusive="true" />
- <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
- <step category="com.android.settings.suggested.category.TRUST_AGENT" />
- <step category="com.android.settings.suggested.category.EMAIL" />
- <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
- multiple="true" />
- <step category="com.android.settings.suggested.category.GESTURE" />
- <step category="com.android.settings.suggested.category.HOTWORD" />
- <step category="com.android.settings.suggested.category.DEFAULT"
- multiple="true" />
- <step category="com.android.settings.suggested.category.SETTINGS_ONLY"
- multiple="true" />
-</optional-steps>
diff --git a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java b/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
deleted file mode 100644
index 14b0d59..0000000
--- a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility.
- */
-public class BluetoothCodecConfig {
- public boolean isMandatoryCodec() { return true; }
- public String getCodecName() { return null; }
- public int getCodecType() { return -1; }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecStatus.java b/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecStatus.java
deleted file mode 100644
index 919ec3f..0000000
--- a/packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecStatus.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility.
- */
-public class BluetoothCodecStatus {
- public BluetoothCodecConfig getCodecConfig() { return null; }
- public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return null; }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
new file mode 100644
index 0000000..152d024
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.settingslib.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class AccessibilityUtilsTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void getEnabledServicesFromSettings_noService_emptyResult() {
+ assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext)).isEmpty();
+ }
+
+ @Test
+ public void getEnabledServicesFromSettings_badFormat_emptyResult() {
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ ":",
+ UserHandle.myUserId());
+
+ assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext)).isEmpty();
+ }
+
+ @Test
+ public void getEnabledServicesFromSettings_1Service_1result() {
+ final ComponentName cn = new ComponentName("pkg", "serv");
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ cn.flattenToString() + ":",
+ UserHandle.myUserId());
+
+ assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
+ .containsExactly(cn);
+ }
+
+ @Test
+ public void getEnabledServicesFromSettings_2Services_2results() {
+ final ComponentName cn1 = new ComponentName("pkg", "serv");
+ final ComponentName cn2 = new ComponentName("pkg", "serv2");
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ cn1.flattenToString() + ":" + cn2.flattenToString(),
+ UserHandle.myUserId());
+
+ assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
+ .containsExactly(cn1, cn2);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
new file mode 100644
index 0000000..996a122
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -0,0 +1,48 @@
+package com.android.settingslib.drawer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
+import android.os.Bundle;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.junit.Test;
+
+@RunWith(RobolectricTestRunner.class)
+public class TileTest {
+
+ private Tile mTile;
+
+ @Before
+ public void setUp() {
+ mTile = new Tile();
+ mTile.metaData = new Bundle();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_profilePrimary_shouldReturnTrue() {
+ mTile.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
+ assertThat(mTile.isPrimaryProfileOnly()).isTrue();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_profileAll_shouldReturnFalse() {
+ mTile.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
+ assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_noExplicitValue_shouldReturnFalse() {
+ assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_nullMetadata_shouldReturnFalse() {
+ mTile.metaData = null;
+ assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index fc1b2238..6e66805 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -52,9 +51,6 @@
import android.util.Pair;
import android.widget.RemoteViews;
-import com.android.settingslib.R;
-import com.android.settingslib.suggestions.SuggestionParser;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,7 +63,6 @@
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -164,35 +159,6 @@
}
@Test
- public void getTilesForIntent_shouldSkipFilteredApps() {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
- URI_GET_SUMMARY);
- addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google");
- addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true");
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- SuggestionParser parser = new SuggestionParser(
- mContext,
- null,
- Collections.emptyList(),
- "0,10");
- parser.filterSuggestions(outTiles, 0, false);
- assertThat(outTiles.size()).isEqualTo(0);
- }
-
- @Test
public void getCategories_shouldHandleExtraIntentAction() {
final String testCategory = "category1";
final String testAction = "action1";
@@ -392,108 +358,6 @@
assertThat(outTiles.size()).isEqualTo(1);
}
- @Test
- public void getTilesForIntent_shouldShowRemoteViewIfSpecified() {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- }
-
- @Test
- public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
- throws RemoteException {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
- null, URI_GET_SUMMARY);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- // Mock the content provider interaction.
- Bundle bundle = new Bundle();
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
- when(mContentResolver.acquireUnstableProvider(anyString()))
- .thenReturn(mIContentProvider);
- when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
- .thenReturn(mIContentProvider);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- // Make sure the summary TextView got a new text string.
- TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
- assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
- assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
- }
-
- @Test
- public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
- throws RemoteException {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
- null, URI_GET_SUMMARY);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- // Mock the content provider interaction.
- Bundle bundle = new Bundle();
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- // Make sure the summary TextView didn't get any text view updates.
- TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
- assertThat(shadowRemoteViews.overrideViewId).isNull();
- assertThat(shadowRemoteViews.overrideText).isNull();
- }
-
public static ResolveInfo newInfo(boolean systemApp, String category) {
return newInfo(systemApp, category, null);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
new file mode 100644
index 0000000..1be856a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.settingslib.net;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkStatsHistory.Entry;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DataUsageControllerTest {
+
+ @Mock
+ private INetworkStatsSession mSession;
+
+ private Context mContext;
+ private DataUsageController mController;
+ private NetworkStatsHistory mNetworkStatsHistory;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = spy(new DataUsageController(mContext));
+ mNetworkStatsHistory = spy(
+ new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
+ doReturn(mNetworkStatsHistory)
+ .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
+ }
+
+ @Test
+ public void getHistoriclUsageLevel_noNetworkSession_shouldReturn0() {
+ doReturn(null).when(mController).getSession();
+
+ assertThat(mController.getHistoriclUsageLevel(null /* template */)).isEqualTo(0L);
+
+ }
+
+ @Test
+ public void getHistoriclUsageLevel_noUsageData_shouldReturn0() {
+ doReturn(mSession).when(mController).getSession();
+
+ assertThat(mController.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ .isEqualTo(0L);
+
+ }
+
+ @Test
+ public void getHistoriclUsageLevel_hasUsageData_shouldReturnTotalUsage() {
+ doReturn(mSession).when(mController).getSession();
+ final long receivedBytes = 743823454L;
+ final long transmittedBytes = 16574289L;
+ final Entry entry = new Entry();
+ entry.bucketStart = 1521583200000L;
+ entry.rxBytes = receivedBytes;
+ entry.txBytes = transmittedBytes;
+ when(mNetworkStatsHistory.getValues(eq(0L), anyLong(), anyLong(), nullable(Entry.class)))
+ .thenReturn(entry);
+
+ assertThat(mController.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ .isEqualTo(receivedBytes + transmittedBytes);
+
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
deleted file mode 100644
index d05bcfd..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.suggestions;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtilsTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowPackageManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class SuggestionParserTest {
-
- private ShadowPackageManager mPackageManager;
- private SuggestionParser mSuggestionParser;
- private SuggestionCategory mMultipleCategory;
- private SuggestionCategory mExclusiveCategory;
- private SuggestionCategory mExpiredExclusiveCategory;
- private List<Tile> mSuggestionsBeforeDismiss;
- private List<Tile> mSuggestionsAfterDismiss;
- private SharedPreferences mPrefs;
- private Tile mSuggestion;
-
- @Before
- public void setUp() {
- mPackageManager = extract(application.getPackageManager());
- mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
- mSuggestion = new Tile();
- mSuggestion.intent = new Intent("action");
- mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
- mSuggestion.metaData = new Bundle();
- mMultipleCategory = new SuggestionCategory();
- mMultipleCategory.category = "category1";
- mMultipleCategory.multiple = true;
- mExclusiveCategory = new SuggestionCategory();
- mExclusiveCategory.category = "category2";
- mExclusiveCategory.exclusive = true;
- mExpiredExclusiveCategory = new SuggestionCategory();
- mExpiredExclusiveCategory.category = "category3";
- mExpiredExclusiveCategory.exclusive = true;
- mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
-
- mSuggestionParser = new SuggestionParser(application, mPrefs,
- Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
- "0");
-
- ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
- info1.activityInfo.packageName = "pkg";
- ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, null);
- infoDupe1.activityInfo.packageName = "pkg";
-
- ResolveInfo info2 = TileUtilsTest.newInfo(true, null);
- info2.activityInfo.packageName = "pkg2";
- ResolveInfo info3 = TileUtilsTest.newInfo(true, null);
- info3.activityInfo.packageName = "pkg3";
- ResolveInfo info4 = TileUtilsTest.newInfo(true, null);
- info4.activityInfo.packageName = "pkg4";
-
- Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
- Intent intent2 = new Intent(Intent.ACTION_MAIN).addCategory("category2");
- Intent intent3 = new Intent(Intent.ACTION_MAIN).addCategory("category3");
-
- mPackageManager.addResolveInfoForIntent(intent1, info1);
- mPackageManager.addResolveInfoForIntent(intent1, info2);
- mPackageManager.addResolveInfoForIntent(intent1, infoDupe1);
- mPackageManager.addResolveInfoForIntent(intent2, info3);
- mPackageManager.addResolveInfoForIntent(intent3, info4);
- }
-
- @Test
- public void dismissSuggestion_shouldDismiss() {
- assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
- }
-
- @Test
- public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
- readAndDismissSuggestion(false);
- mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
- assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(1);
- assertThat(mSuggestionsBeforeDismiss.get(1)).isEqualTo(mSuggestionsAfterDismiss.get(0));
- }
-
- @Test
- public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
- readAndDismissSuggestion(true);
- assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(1);
- }
-
- @Test
- public void testGetSuggestion_exclusiveNotAvailable_onlyRegularCategoryAndNoDupe() {
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category2"),
- "pkg3");
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category3"),
- "pkg4");
-
- // If exclusive item is not available, the other categories should be shown
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
- assertThat(suggestions).hasSize(2);
-
- assertThat(suggestions.get(0).intent.getComponent().getPackageName()).isEqualTo("pkg");
- assertThat(suggestions.get(1).intent.getComponent().getPackageName()).isEqualTo("pkg2");
- }
-
- @Test
- public void testGetSuggestion_exclusiveExpiredAvailable_shouldLoadWithRegularCategory() {
- // First remove permanent exclusive
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category2"),
- "pkg3");
- // Set the other exclusive to be expired.
- mPrefs.edit()
- .putLong(mExpiredExclusiveCategory.category + "_setup_time",
- System.currentTimeMillis() - 1000)
- .commit();
-
- // If exclusive is expired, they should be shown together with the other categories
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(true /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
-
- assertThat(suggestions).hasSize(3);
- }
-
- @Test
- public void testGetSuggestions_exclusive() {
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
-
- assertThat(suggestions).hasSize(1);
- }
-
- @Test
- public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
- final Tile suggestion = new Tile();
- suggestion.metaData = new Bundle();
- suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
- suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
-
- // Dismiss suggestion when smart suggestion is not enabled.
- mSuggestionParser.dismissSuggestion(suggestion);
-
- assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
- .isTrue();
- }
-
- private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
- mSuggestionsBeforeDismiss = new ArrayList<>();
- mSuggestionsAfterDismiss = new ArrayList<>();
- mSuggestionParser.readSuggestions(
- mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
-
- final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
- if (mSuggestionParser.dismissSuggestion(suggestion)) {
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
- suggestion.intent.getComponent().getPackageName());
- }
- mSuggestionParser.readSuggestions(
- mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
- }
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f728684..9d398b5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -841,7 +841,19 @@
WifiConfiguration config = WifiConfiguration
.getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
+ int originalApBand = config.apBand;
mWifiManager.setWifiApConfiguration(config);
+
+ // Depending on device hardware, we may need to notify the user of a setting change for
+ // the apBand preference
+ boolean dualMode = mWifiManager.isDualModeSupported();
+ int storedApBand = mWifiManager.getWifiApConfiguration().apBand;
+ if (dualMode) {
+ if (storedApBand != originalApBand) {
+ Log.d(TAG, "restored ap configuration requires a conversion, notify the user");
+ mWifiManager.notifyUserOfApBandConversion();
+ }
+ }
} catch (IOException | BackupUtils.BadVersionException e) {
Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index beb4e9e..eab4b97 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -120,6 +120,7 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<uses-permission android:name="android.permission.TRUST_LISTENER" />
+ <uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
<uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 513d848..6bc0965 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -381,7 +381,10 @@
</plurals>
<!-- Fingerprint hint message when finger was not recognized.-->
- <string name="fingerprint_not_recognized">Not recognized</string>
+ <string name="kg_fingerprint_not_recognized">Not recognized</string>
+
+ <!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
+ <string name="kg_face_not_recognized">Not recognized</string>
<!-- Instructions telling the user remaining times when enter SIM PIN view. -->
<plurals name="kg_password_default_pin_message">
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 88d19f4..28466fd 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -161,30 +161,31 @@
style="@style/TextAppearance.NotificationInfo.Button"/>
</LinearLayout>
</LinearLayout>
- <RelativeLayout
+ <com.android.systemui.statusbar.NotificationUndoLayout
android:id="@+id/confirmation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_guts_button_spacing"
- android:layout_marginTop="@dimen/notification_guts_button_spacing"
- android:layout_marginStart="@dimen/notification_guts_button_side_margin"
- android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
android:visibility="gone"
android:orientation="horizontal" >
<TextView
android:id="@+id/confirmation_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
+ android:layout_gravity="start|center_vertical"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
android:text="@string/notification_channel_disabled"
style="@style/TextAppearance.NotificationInfo.Confirmation"/>
<TextView
android:id="@+id/undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
+ 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"
+ android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
+ android:layout_gravity="end|center_vertical"
android:text="@string/inline_undo"
style="@style/TextAppearance.NotificationInfo.Button"/>
- </RelativeLayout>
+ </com.android.systemui.statusbar.NotificationUndoLayout>
</com.android.systemui.statusbar.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index 25b117f..f13f019 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -43,7 +43,7 @@
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
@@ -86,7 +86,7 @@
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_home_bg_light"
@@ -129,7 +129,7 @@
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_recents_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index 367c13c..420b072 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -39,7 +39,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_recents_bg_light"
@@ -79,7 +79,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_home_bg_light"
@@ -120,7 +120,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index bac02aa..f9308cd 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -39,7 +39,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
@@ -82,7 +82,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+ android:theme="@style/ScreenPinningRequestTheme" >
<ImageView
android:id="@+id/screen_pinning_home_bg_light"
@@ -125,7 +125,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_recents_bg_light"
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
index 8b56b68..42d541e3 100644
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -58,6 +58,7 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
+ android:paddingStart="2.5dp"
android:paddingEnd="1dp"
android:visibility="gone" />
<Space
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index c59dbdc..bb0c6f6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,8 +24,6 @@
<dimen name="brightness_mirror_height">40dp</dimen>
- <!-- Width for the spacer, used between QS tiles. -->
- <dimen name="qs_quick_tile_space_width">38dp</dimen>
<dimen name="qs_tile_margin_top">2dp</dimen>
<dimen name="qs_header_tooltip_height">24dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 2dc6525..eb5c180 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -17,7 +17,7 @@
-->
<resources>
<!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
+ <dimen name="notification_panel_width">416dp</dimen>
<!-- Diameter of outer shape drawable shown in navbar search-->
<dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 791d761..23b64e3 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
- <item name="android:layout_width">@dimen/standard_notification_panel_width</item>
+ <item name="android:layout_width">@dimen/notification_panel_width</item>
</style>
<style name="UserDetailView">
diff --git a/packages/SystemUI/res/values-sw900dp-land/dimen.xml b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
new file mode 100644
index 0000000..ac7e6b8
--- /dev/null
+++ b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2012, 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>
+ <!-- Standard notification width + gravity for tablet large screen device -->
+ <dimen name="notification_panel_width">544dp</dimen>
+
+ <!-- Maximum width of quick quick settings panel. -->
+ <dimen name="qs_quick_layout_width">478dp</dimen>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
index eaca9d7..2c66454 100644
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -18,4 +18,7 @@
<resources>
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">544dp</dimen>
+
+ <!-- Maximum width of quick quick settings panel. -->
+ <dimen name="qs_quick_layout_width">478dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3b1872a..aecf494 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -104,6 +104,9 @@
prevent wasting cpu cycles on the dimming animation -->
<bool name="config_navigation_bar_enable_auto_dim_no_visible_wallpaper">true</bool>
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_columns">6</integer>
+
<!-- Whether QuickSettings is in a phone landscape -->
<bool name="quick_settings_wide">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3b58b72..e45c367 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -55,7 +55,7 @@
<dimen name="status_bar_left_clock_end_padding">7dp</dimen>
<!-- Spacing after the wifi signals that is present if there are any icons following it. -->
- <dimen name="status_bar_wifi_signal_spacer_width">4dp</dimen>
+ <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen>
<!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
<dimen name="status_bar_airplane_spacer_width">4dp</dimen>
@@ -277,7 +277,6 @@
<!-- Width for the notification panel and related windows -->
<dimen name="match_parent">-1px</dimen>
- <dimen name="standard_notification_panel_width">416dp</dimen>
<!-- The top margin of the panel that holds the list of notifications. -->
<dimen name="notification_panel_margin_top">0dp</dimen>
@@ -349,8 +348,8 @@
<dimen name="qs_tile_margin_top_bottom">12dp</dimen>
<dimen name="qs_tile_margin_top">18dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
- <!-- Width for the spacer, used between QS tiles. -->
- <dimen name="qs_quick_tile_space_width">0dp</dimen>
+ <!-- Maximum width of quick quick settings panel. Defaults to MATCH_PARENT-->
+ <dimen name="qs_quick_layout_width">-1px</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
@@ -764,9 +763,6 @@
<dimen name="volume_expander_margin_end">2dp</dimen>
<dimen name="volume_expander_margin_top">6dp</dimen>
- <!-- Padding between icon and text for managed profile toast -->
- <dimen name="managed_profile_toast_padding">4dp</dimen>
-
<!-- Thickness of the assist disclosure beams -->
<dimen name="assist_disclosure_thickness">2.5dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 84a76bc..915fc6e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1326,9 +1326,6 @@
<!-- Hide quick settings tile confirmation button -->
<string name="quick_settings_reset_confirmation_button">Hide</string>
- <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
- <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
-
<!-- volume stream names. All nouns. -->
<string name="stream_voice_call">Call</string> <!-- STREAM_VOICE_CALL -->
<string name="stream_system">System</string> <!-- STREAM_SYSTEM -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e4f5989..988a516 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -403,6 +403,9 @@
<item name="fillColor">?android:attr/textColorPrimary</item>
<item name="singleToneColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="ScreenPinningRequestTheme" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
+ </style>
<style name="TextAppearance.Volume">
<item name="android:textStyle">normal</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index cd831d1..d38cc0f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -54,6 +54,7 @@
public static final int HIT_TARGET_HOME = 2;
public static final int HIT_TARGET_OVERVIEW = 3;
public static final int HIT_TARGET_ROTATION = 4;
+ public static final int HIT_TARGET_DEAD_ZONE = 5;
@Retention(RetentionPolicy.SOURCE)
@IntDef({FLAG_DISABLE_SWIPE_UP,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 62b8e7c..0340904 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -27,5 +27,5 @@
*/
public static final boolean DEBUG = false;
public static final boolean DEBUG_SIM_STATES = true;
- public static final boolean DEBUG_FP_WAKELOCK = true;
+ public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 4e060f6..d5d96fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -73,6 +73,7 @@
private ArraySet<View> mVisibleInDoze;
private boolean mPulsing;
+ private boolean mWasPulsing;
private float mDarkAmount = 0;
private int mTextColor;
private float mWidgetPadding;
@@ -224,7 +225,8 @@
boolean hasHeader = mKeyguardSlice.hasHeader();
boolean smallClock = hasHeader || mPulsing;
long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
- long delay = smallClock ? 0 : duration / 4;
+ long delay = smallClock || mWasPulsing ? 0 : duration / 4;
+ mWasPulsing = false;
boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
&& mKeyguardSlice.getLayoutTransition().isRunning();
@@ -448,6 +450,12 @@
}
public void setPulsing(boolean pulsing, boolean animate) {
+ if (mPulsing == pulsing) {
+ return;
+ }
+ if (mPulsing) {
+ mWasPulsing = true;
+ }
mPulsing = pulsing;
mKeyguardSlice.setPulsing(pulsing, animate);
updateDozeVisibleViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d9a1b11..d639fbf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -39,6 +39,7 @@
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -48,6 +49,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -72,7 +74,6 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -145,26 +146,26 @@
private static final int MSG_DREAMING_STATE_CHANGED = 333;
private static final int MSG_USER_UNLOCKED = 334;
private static final int MSG_ASSISTANT_STACK_CHANGED = 335;
- private static final int MSG_FINGERPRINT_AUTHENTICATION_CONTINUE = 336;
+ private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
- /** Fingerprint state: Not listening to fingerprint. */
- private static final int FINGERPRINT_STATE_STOPPED = 0;
+ /** Biometric authentication state: Not listening. */
+ private static final int BIOMETRIC_STATE_STOPPED = 0;
- /** Fingerprint state: Listening. */
- private static final int FINGERPRINT_STATE_RUNNING = 1;
+ /** Biometric authentication state: Listening. */
+ private static final int BIOMETRIC_STATE_RUNNING = 1;
/**
- * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to
+ * Biometric authentication: Cancelling and waiting for the relevant biometric service to
* send us the confirmation that cancellation has happened.
*/
- private static final int FINGERPRINT_STATE_CANCELLING = 2;
+ private static final int BIOMETRIC_STATE_CANCELLING = 2;
/**
- * Fingerprint state: During cancelling we got another request to start listening, so when we
+ * Biometric state: During cancelling we got another request to start listening, so when we
* receive the cancellation done signal, we should start listening again.
*/
- private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3;
+ private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
@@ -227,7 +228,8 @@
private List<SubscriptionInfo> mSubscriptionInfo;
private TrustManager mTrustManager;
private UserManager mUserManager;
- private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
+ private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private LockPatternUtils mLockPatternUtils;
private final IDreamManager mDreamManager;
private boolean mIsDreaming;
@@ -235,14 +237,15 @@
private boolean mLogoutEnabled;
/**
- * Short delay before restarting fingerprint authentication after a successful try
- * This should be slightly longer than the time between onFingerprintAuthenticated and
- * setKeyguardGoingAway(true).
+ * Short delay before restarting biometric authentication after a successful try
+ * This should be slightly longer than the time between on<biometric>Authenticated
+ * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
*/
- private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
+ private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
// If FP daemon dies, keyguard should retry after a short delay
- private int mHardwareUnavailableRetryCount = 0;
+ private int mHardwareFingerprintUnavailableRetryCount = 0;
+ private int mHardwareFaceUnavailableRetryCount = 0;
private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
@@ -333,10 +336,10 @@
break;
case MSG_ASSISTANT_STACK_CHANGED:
mAssistantVisible = (boolean)msg.obj;
- updateFingerprintListeningState();
+ updateBiometricListeningState();
break;
- case MSG_FINGERPRINT_AUTHENTICATION_CONTINUE:
- updateFingerprintListeningState();
+ case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
+ updateBiometricListeningState();
break;
case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
updateLogoutEnabled();
@@ -359,10 +362,11 @@
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
+ private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
private static int sCurrentUser;
- private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
+ private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
private static boolean sDisableHandlerCheckForTesting;
public synchronized static void setCurrentUser(int currentUser) {
@@ -487,7 +491,7 @@
*/
public void setKeyguardOccluded(boolean occluded) {
mKeyguardOccluded = occluded;
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
/**
@@ -515,19 +519,19 @@
mUserFingerprintAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
- mTrustManager.unlockedByFingerprintForUser(userId);
+ mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAuthenticated(userId);
+ cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
}
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE),
- FINGERPRINT_CONTINUE_DELAY_MS);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+ BIOMETRIC_CONTINUE_DELAY_MS);
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
@@ -539,10 +543,10 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAuthFailed();
+ cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
}
}
- handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
+ handleFingerprintHelp(-1, mContext.getString(R.string.kg_fingerprint_not_recognized));
}
private void handleFingerprintAcquired(int acquireInfo) {
@@ -552,7 +556,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAcquired();
+ cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
}
}
}
@@ -577,7 +581,7 @@
}
onFingerprintAuthenticated(userId);
} finally {
- setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
Trace.endSection();
}
@@ -586,7 +590,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintHelp(msgId, helpString);
+ cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT);
}
}
}
@@ -595,23 +599,23 @@
@Override
public void run() {
Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
- mHardwareUnavailableRetryCount);
+ mHardwareFingerprintUnavailableRetryCount);
updateFingerprintListeningState();
}
};
private void handleFingerprintError(int msgId, String errString) {
if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
- && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
- setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
startListeningForFingerprint();
} else {
- setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
- if (mHardwareUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
- mHardwareUnavailableRetryCount++;
+ if (mHardwareFingerprintUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+ mHardwareFingerprintUnavailableRetryCount++;
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
mHandler.postDelayed(mRetryFingerprintAuthentication, HW_UNAVAILABLE_TIMEOUT);
}
@@ -626,7 +630,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintError(msgId, errString);
+ cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
}
}
}
@@ -636,8 +640,8 @@
}
private void setFingerprintRunningState(int fingerprintRunningState) {
- boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
- boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+ boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+ boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
mFingerprintRunningState = fingerprintRunningState;
// Clients of KeyguardUpdateMonitor don't care about the internal state about the
@@ -653,10 +657,162 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning());
+ cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(),
+ BiometricSourceType.FINGERPRINT);
}
}
}
+
+ private void onFaceAuthenticated(int userId) {
+ Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
+ mUserFaceAuthenticated.put(userId, true);
+ // Update/refresh trust state only if user can skip bouncer
+ if (getUserCanSkipBouncer(userId)) {
+ mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
+ }
+ // Don't send cancel if authentication succeeds
+ mFaceCancelSignal = null;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricAuthenticated(userId,
+ BiometricSourceType.FACE);
+ }
+ }
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+ BIOMETRIC_CONTINUE_DELAY_MS);
+
+ // Only authenticate face once when assistant is visible
+ mAssistantVisible = false;
+
+ Trace.endSection();
+ }
+
+ private void handleFaceAuthFailed() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricAuthFailed(BiometricSourceType.FACE);
+ }
+ }
+ handleFaceHelp(-1, mContext.getString(R.string.kg_face_not_recognized));
+ }
+
+ private void handleFaceAcquired(int acquireInfo) {
+ if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
+ return;
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricAcquired(BiometricSourceType.FACE);
+ }
+ }
+ }
+
+ private void handleFaceAuthenticated(int authUserId) {
+ Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
+ try {
+ final int userId;
+ try {
+ userId = ActivityManager.getService().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get current user id: ", e);
+ return;
+ }
+ if (userId != authUserId) {
+ Log.d(TAG, "Face authenticated for wrong user: " + authUserId);
+ return;
+ }
+ if (isFaceDisabled(userId)) {
+ Log.d(TAG, "Face authentication disabled by DPM for userId: " + userId);
+ return;
+ }
+ onFaceAuthenticated(userId);
+ } finally {
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ }
+ Trace.endSection();
+ }
+
+ private void handleFaceHelp(int msgId, String helpString) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE);
+ }
+ }
+ }
+
+ private Runnable mRetryFaceAuthentication = new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "Retrying face after HW unavailable, attempt " +
+ mHardwareFaceUnavailableRetryCount);
+ updateFaceListeningState();
+ }
+ };
+
+ private void handleFaceError(int msgId, String errString) {
+ if (msgId == FaceManager.FACE_ERROR_CANCELED
+ && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ startListeningForFace();
+ } else {
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ }
+
+ if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE) {
+ if (mHardwareFaceUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+ mHardwareFaceUnavailableRetryCount++;
+ mHandler.removeCallbacks(mRetryFaceAuthentication);
+ mHandler.postDelayed(mRetryFaceAuthentication, HW_UNAVAILABLE_TIMEOUT);
+ }
+ }
+
+ if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ mLockPatternUtils.requireStrongAuth(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+ getCurrentUser());
+ }
+
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricError(msgId, errString,
+ BiometricSourceType.FACE);
+ }
+ }
+ }
+
+ private void handleFaceLockoutReset() {
+ updateFaceListeningState();
+ }
+
+ private void setFaceRunningState(int faceRunningState) {
+ boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING;
+ boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING;
+ mFaceRunningState = faceRunningState;
+
+ // Clients of KeyguardUpdateMonitor don't care about the internal state or about the
+ // asynchronousness of the cancel cycle. So only notify them if the actualy running state
+ // has changed.
+ if (wasRunning != isRunning) {
+ notifyFaceRunningStateChanged();
+ }
+ }
+
+ private void notifyFaceRunningStateChanged() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
+ BiometricSourceType.FACE);
+ }
+ }
+ }
+
private void handleFaceUnlockStateChanged(boolean running, int userId) {
checkIsHandlerThread();
mUserFaceUnlockRunning.put(userId, running);
@@ -673,7 +829,11 @@
}
public boolean isFingerprintDetectionRunning() {
- return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+ return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+ }
+
+ public boolean isFaceDetectionRunning() {
+ return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
}
private boolean isTrustDisabled(int userId) {
@@ -692,9 +852,18 @@
|| isSimPinSecure();
}
+ private boolean isFaceDisabled(int userId) {
+ final DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId)
+ & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0
+ || isSimPinSecure();
+ }
+
+
public boolean getUserCanSkipBouncer(int userId) {
return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
- && isUnlockingWithFingerprintAllowed());
+ && isUnlockingWithBiometricAllowed());
}
public boolean getUserHasTrust(int userId) {
@@ -705,8 +874,8 @@
return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
}
- public boolean isUnlockingWithFingerprintAllowed() {
- return mStrongAuthTracker.isUnlockingWithFingerprintAllowed();
+ public boolean isUnlockingWithBiometricAllowed() {
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
}
public boolean isUserInLockdown(int userId) {
@@ -862,7 +1031,7 @@
}
};
- private final FingerprintManager.LockoutResetCallback mLockoutResetCallback
+ private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
= new FingerprintManager.LockoutResetCallback() {
@Override
public void onLockoutReset() {
@@ -870,13 +1039,21 @@
}
};
- private FingerprintManager.AuthenticationCallback mAuthenticationCallback
+ private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
+ = new FaceManager.LockoutResetCallback() {
+ @Override
+ public void onLockoutReset() {
+ handleFaceLockoutReset();
+ }
+ };
+
+ private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
@Override
public void onAuthenticationFailed() {
handleFingerprintAuthFailed();
- };
+ }
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
@@ -900,8 +1077,42 @@
handleFingerprintAcquired(acquireInfo);
}
};
+
+ private FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+ = new FaceManager.AuthenticationCallback() {
+
+ @Override
+ public void onAuthenticationFailed() {
+ handleFaceAuthFailed();
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
+ handleFaceAuthenticated(result.getUserId());
+ Trace.endSection();
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ handleFaceHelp(helpMsgId, helpString.toString());
+ }
+
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ handleFaceError(errMsgId, errString.toString());
+ }
+
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {
+ handleFaceAcquired(acquireInfo);
+ }
+ };
+
private CancellationSignal mFingerprintCancelSignal;
+ private CancellationSignal mFaceCancelSignal;
private FingerprintManager mFpm;
+ private FaceManager mFaceAuthenticationManager;
/**
* When we receive a
@@ -1049,9 +1260,9 @@
super(context);
}
- public boolean isUnlockingWithFingerprintAllowed() {
+ public boolean isUnlockingWithBiometricAllowed() {
int userId = getCurrentUser();
- return isFingerprintAllowedForUser(userId);
+ return isBiometricAllowedForUser(userId);
}
public boolean hasUserAuthenticatedSinceBoot() {
@@ -1075,7 +1286,7 @@
protected void handleStartedWakingUp() {
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
- updateFingerprintListeningState();
+ updateBiometricListeningState();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1087,7 +1298,7 @@
}
protected void handleStartedGoingToSleep(int arg1) {
- clearFingerprintRecognized();
+ clearBiometricRecognized();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1096,7 +1307,7 @@
}
}
mGoingToSleep = true;
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
protected void handleFinishedGoingToSleep(int arg1) {
@@ -1108,7 +1319,7 @@
cb.onFinishedGoingToSleep(arg1);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
private void handleScreenTurnedOn() {
@@ -1122,7 +1333,8 @@
}
private void handleScreenTurnedOff() {
- mHardwareUnavailableRetryCount = 0;
+ mHardwareFingerprintUnavailableRetryCount = 0;
+ mHardwareFaceUnavailableRetryCount = 0;
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1141,7 +1353,7 @@
cb.onDreamingStateChanged(mIsDreaming);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
private void handleUserInfoChanged(int userId) {
@@ -1238,9 +1450,17 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
}
- updateFingerprintListeningState();
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ mFaceAuthenticationManager =
+ (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+ }
+ updateBiometricListeningState();
if (mFpm != null) {
- mFpm.addLockoutResetCallback(mLockoutResetCallback);
+ mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
+ }
+ if (mFaceAuthenticationManager != null) {
+ mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -1249,28 +1469,55 @@
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
}
+ private void updateBiometricListeningState() {
+ updateFingerprintListeningState();
+ updateFaceListeningState();
+ }
+
private void updateFingerprintListeningState() {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
- if (mHandler.hasMessages(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE)) {
+ if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
return;
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
- if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
+ if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
stopListeningForFingerprint();
- } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
+ } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
&& shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
+ private void updateFaceListeningState() {
+ // If this message exists, we should not authenticate again until this message is
+ // consumed by the handler
+ if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+ return;
+ }
+ mHandler.removeCallbacks(mRetryFaceAuthentication);
+ boolean shouldListenForFace = shouldListenForFace();
+ if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
+ stopListeningForFace();
+ } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING
+ && shouldListenForFace) {
+ startListeningForFace();
+ }
+ }
+
private boolean shouldListenForFingerprintAssistant() {
return mAssistantVisible && mKeyguardOccluded
&& !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
+ private boolean shouldListenForFaceAssistant() {
+ return mAssistantVisible && mKeyguardOccluded
+ && !mUserFaceAuthenticated.get(getCurrentUser(), false)
+ && !mUserHasTrust.get(getCurrentUser(), false);
+ }
+
private boolean shouldListenForFingerprint() {
return (mKeyguardIsVisible || !mDeviceInteractive ||
(mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
@@ -1279,9 +1526,18 @@
&& !mKeyguardGoingAway;
}
+ private boolean shouldListenForFace() {
+ return (mKeyguardIsVisible || !mDeviceInteractive ||
+ (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
+ shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
+ && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
+ && !mKeyguardGoingAway;
+ }
+
+
private void startListeningForFingerprint() {
- if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
- setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
+ setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
@@ -1291,9 +1547,27 @@
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
- mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null,
- userId);
- setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
+ mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
+ null, userId);
+ setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
+ }
+ }
+
+ private void startListeningForFace() {
+ if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
+ setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "startListeningForFace()");
+ int userId = ActivityManager.getCurrentUser();
+ if (isUnlockWithFacePossible(userId)) {
+ if (mFaceCancelSignal != null) {
+ mFaceCancelSignal.cancel();
+ }
+ mFaceCancelSignal = new CancellationSignal();
+ mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+ mFaceAuthenticationCallback, null, userId);
+ setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -1302,17 +1576,37 @@
&& mFpm.getEnrolledFingerprints(userId).size() > 0;
}
+ public boolean isUnlockWithFacePossible(int userId) {
+ return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+ && !isFaceDisabled(userId)
+ && mFaceAuthenticationManager.hasEnrolledFace(userId);
+ }
+
private void stopListeningForFingerprint() {
if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
- if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
+ if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING) {
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
mFingerprintCancelSignal = null;
}
- setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+ setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
}
- if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
- setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
+ }
+ }
+
+ private void stopListeningForFace() {
+ if (DEBUG) Log.v(TAG, "stopListeningForFace()");
+ if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) {
+ if (mFaceCancelSignal != null) {
+ mFaceCancelSignal.cancel();
+ mFaceCancelSignal = null;
+ }
+ setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
+ }
+ if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
}
}
@@ -1590,6 +1884,10 @@
}
}
+ public boolean isKeyguardVisible() {
+ return mKeyguardIsVisible;
+ }
+
/**
* Notifies that the visibility state of Keyguard has changed.
*
@@ -1605,7 +1903,7 @@
cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
/**
@@ -1613,7 +1911,7 @@
*/
private void handleKeyguardReset() {
if (DEBUG) Log.d(TAG, "handleKeyguardReset");
- updateFingerprintListeningState();
+ updateBiometricListeningState();
mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
}
@@ -1642,7 +1940,7 @@
cb.onKeyguardBouncerChanged(isBouncer);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
/**
@@ -1725,7 +2023,7 @@
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post if first
- mHandler.post(mUpdateFingerprintListeningState);
+ mHandler.post(mUpdateBiometricListeningState);
}
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
@@ -1813,9 +2111,11 @@
mFailedAttempts.put(userId, getFailedUnlockAttempts(userId) + 1);
}
- public void clearFingerprintRecognized() {
+ public void clearBiometricRecognized() {
mUserFingerprintAuthenticated.clear();
- mTrustManager.clearAllFingerprints();
+ mUserFaceAuthenticated.clear();
+ mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
+ mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE);
}
public boolean isSimPinVoiceSecure() {
@@ -2050,7 +2350,7 @@
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
- pw.println(" allowed=" + isUnlockingWithFingerprintAllowed());
+ pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId));
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
@@ -2059,5 +2359,18 @@
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
+ if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+ final int userId = ActivityManager.getCurrentUser();
+ final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ pw.println(" Face authentication state (user=" + userId + ")");
+ pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
+ pw.println(" auth'd=" + mUserFaceAuthenticated.get(userId));
+ pw.println(" authSinceBoot="
+ + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+ pw.println(" disabled(DPM)=" + isFaceDisabled(userId));
+ pw.println(" possible=" + isUnlockWithFacePossible(userId));
+ pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
+ pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 67571bb..8135ac5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -17,7 +17,7 @@
import android.app.admin.DevicePolicyManager;
import android.graphics.Bitmap;
-import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.media.AudioManager;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
@@ -213,38 +213,46 @@
public void onTrustGrantedWithFlags(int flags, int userId) { }
/**
- * Called when a finger has been acquired.
+ * Called when a biometric has been acquired.
* <p>
- * It is guaranteed that either {@link #onFingerprintAuthenticated} or
- * {@link #onFingerprintAuthFailed()} is called after this method eventually.
+ * It is guaranteed that either {@link #onBiometricAuthenticated} or
+ * {@link #onBiometricAuthFailed(BiometricSourceType)} is called after this method eventually.
+ * @param biometricSourceType
*/
- public void onFingerprintAcquired() { }
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType) { }
/**
- * Called when a fingerprint couldn't be authenticated.
+ * Called when a biometric couldn't be authenticated.
+ * @param biometricSourceType
*/
- public void onFingerprintAuthFailed() { }
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }
/**
- * Called when a fingerprint is recognized.
- * @param userId the user id for which the fingerprint was authenticated
+ * Called when a biometric is recognized.
+ * @param userId the user id for which the biometric sample was authenticated
+ * @param biometricSourceType
*/
- public void onFingerprintAuthenticated(int userId) { }
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
/**
- * Called when fingerprint provides help string (e.g. "Try again")
+ * Called when biometric authentication provides help string (e.g. "Try again")
* @param msgId
* @param helpString
+ * @param biometricSourceType
*/
- public void onFingerprintHelp(int msgId, String helpString) { }
+ public void onBiometricHelp(int msgId, String helpString,
+ BiometricSourceType biometricSourceType) { }
/**
- * Called when fingerprint provides an semi-permanent error message
- * (e.g. "Hardware not available").
- * @param msgId one of the error messages listed in {@link FingerprintManager}
+ * Called when biometric authentication method provides a semi-permanent
+ * error message (e.g. "Hardware not available").
+ * @param msgId one of the error messages listed in
+ * {@link android.hardware.biometrics.BiometricConstants}
* @param errString
+ * @param biometricSourceType
*/
- public void onFingerprintError(int msgId, String errString) { }
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) { }
/**
* Called when the state of face unlock changed.
@@ -252,9 +260,10 @@
public void onFaceUnlockStateChanged(boolean running, int userId) { }
/**
- * Called when the fingerprint running state changed.
+ * Called when biometric running state changed.
*/
- public void onFingerprintRunningStateChanged(boolean running) { }
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) { }
/**
* Called when the state that the user hasn't used strong authentication since quite some time
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 61784fa..3ac6705 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -55,6 +55,7 @@
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.Utils.DisableStateTracker;
+import com.android.systemui.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -74,6 +75,7 @@
private int mTextColor;
private int mLevel;
private boolean mForceShowPercent;
+ private boolean mShowPercentAvailable;
private int mDarkModeBackgroundColor;
private int mDarkModeFillColor;
@@ -113,6 +115,9 @@
atts.recycle();
mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
+ mShowPercentAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+
addOnAttachStateChangeListener(
new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
@@ -264,8 +269,11 @@
private void updateShowPercent() {
final boolean showing = mBatteryPercentView != null;
- if (0 != Settings.System.getIntForUser(getContext().getContentResolver(),
- SHOW_BATTERY_PERCENT, 0, mUser) || mForceShowPercent) {
+ final boolean systemSetting = 0 != Settings.System
+ .getIntForUser(getContext().getContentResolver(),
+ SHOW_BATTERY_PERCENT, 0, mUser);
+
+ if ((mShowPercentAvailable && systemSetting) || mForceShowPercent) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index cbb69ee..1e458fa 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,7 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.internal.util.LatencyTracker;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.StatusBar;
/**
@@ -72,10 +73,10 @@
}
private void fakeWakeAndUnlock() {
- FingerprintUnlockController fingerprintUnlockController = getComponent(StatusBar.class)
- .getFingerprintUnlockController();
- fingerprintUnlockController.onFingerprintAcquired();
- fingerprintUnlockController.onFingerprintAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser());
+ BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class)
+ .getBiometricUnlockController();
+ biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+ biometricUnlockController.onBiometricAuthenticated(
+ KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 3b2c9df..bbbc71f 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -26,8 +26,12 @@
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import android.annotation.Dimension;
+import android.app.ActivityManager;
import android.app.Fragment;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Canvas;
@@ -41,7 +45,6 @@
import android.hardware.display.DisplayManager;
import android.os.SystemProperties;
import android.provider.Settings.Secure;
-import androidx.annotation.VisibleForTesting;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
@@ -68,6 +71,8 @@
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.RotationUtils;
+import androidx.annotation.VisibleForTesting;
+
/**
* An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
* for antialiasing and emulation purposes.
@@ -91,6 +96,7 @@
private int mRotation;
private DisplayCutoutView mCutoutTop;
private DisplayCutoutView mCutoutBottom;
+ private SecureSetting mColorInversionSetting;
@Override
public void start() {
@@ -164,22 +170,19 @@
Dependency.get(TunerService.class).addTunable(this, SIZE);
// Watch color inversion and invert the overlay as needed.
- SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
+ mColorInversionSetting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER),
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
- int tint = value != 0 ? Color.WHITE : Color.BLACK;
- ColorStateList tintList = ColorStateList.valueOf(tint);
- ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
- ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
- ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
- ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
- mCutoutTop.setColor(tint);
- mCutoutBottom.setColor(tint);
+ updateColorInversion(value);
}
};
- setting.setListening(true);
- setting.onChange(false);
+ mColorInversionSetting.setListening(true);
+ mColorInversionSetting.onChange(false);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mIntentReceiver, filter);
mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
@@ -199,6 +202,31 @@
});
}
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+ int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ ActivityManager.getCurrentUser());
+ // update color inversion setting to the new user
+ mColorInversionSetting.setUserId(newUserId);
+ updateColorInversion(mColorInversionSetting.getValue());
+ }
+ }
+ };
+
+ private void updateColorInversion(int colorsInvertedValue) {
+ int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
+ ColorStateList tintList = ColorStateList.valueOf(tint);
+ ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+ ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+ ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
+ ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
+ mCutoutTop.setColor(tint);
+ mCutoutBottom.setColor(tint);
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
updateOrientation();
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 9a43d9e..3c8a461 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
@@ -46,7 +45,7 @@
public class SysuiColorExtractor extends ColorExtractor implements Dumpable {
private static final String TAG = "SysuiColorExtractor";
private boolean mWallpaperVisible;
- private boolean mMediaBackdropVisible;
+ private boolean mHasBackdrop;
// Colors to return when the wallpaper isn't visible
private final GradientColors mWpHiddenColors;
@@ -165,7 +164,7 @@
return mWpHiddenColors;
}
} else {
- if (mMediaBackdropVisible) {
+ if (mHasBackdrop) {
return mWpHiddenColors;
} else {
return super.getColors(which, type);
@@ -181,9 +180,9 @@
}
}
- public void setMediaBackdropVisible(boolean visible) {
- if (mMediaBackdropVisible != visible) {
- mMediaBackdropVisible = visible;
+ public void setHasBackdrop(boolean hasBackdrop) {
+ if (mHasBackdrop != hasBackdrop) {
+ mHasBackdrop = hasBackdrop;
triggerColorsChanged(WallpaperManager.FLAG_LOCK);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index cf6f16a..b9d1021 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -30,6 +30,7 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -82,8 +83,8 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -522,18 +523,18 @@
}
@Override
- public void onFingerprintAuthFailed() {
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
if (mLockPatternUtils.isSecure(currentUser)) {
- mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+ mLockPatternUtils.getDevicePolicyManager().reportFailedBiometricAttempt(
currentUser);
}
}
@Override
- public void onFingerprintAuthenticated(int userId) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
if (mLockPatternUtils.isSecure(userId)) {
- mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+ mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt(
userId);
}
}
@@ -1645,7 +1646,7 @@
}
mUpdateMonitor.clearFailedUnlockAttempts();
- mUpdateMonitor.clearFingerprintRecognized();
+ mUpdateMonitor.clearBiometricRecognized();
if (mGoingToSleep) {
Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
@@ -2051,9 +2052,9 @@
public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
ViewGroup container, NotificationPanelView panelView,
- FingerprintUnlockController fingerprintUnlockController) {
+ BiometricUnlockController biometricUnlockController) {
mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, panelView,
- fingerprintUnlockController, mDismissCallbackRegistry);
+ biometricUnlockController, mDismissCallbackRegistry);
return mStatusBarKeyguardViewManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 615b29f..2dc531a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -229,7 +229,9 @@
mSettingsButton = findViewById(R.id.settings);
mSettingsButton.setAlpha(0);
mSettingsButton.setOnClickListener((v) -> {
- showSettings();
+ if (v.getAlpha() != 0) {
+ showSettings();
+ }
});
mDismissButton = findViewById(R.id.dismiss);
mDismissButton.setAlpha(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 100751c..f13f489 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -189,8 +189,9 @@
// marquee. This will ensure that accessibility doesn't announce the TYPE_VIEW_SELECTED
// event on any of the children.
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ int currentItem = isLayoutRtl() ? mPages.size() - 1 - getCurrentItem() : getCurrentItem();
for (int i = 0; i < mPages.size(); i++) {
- mPages.get(i).setSelected(i == getCurrentItem() ? selected : false);
+ mPages.get(i).setSelected(i == currentItem ? selected : false);
}
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index c9c04d9..1c50f79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -45,6 +45,7 @@
public static final String NUM_QUICK_TILES = "sysui_qqs_count";
private boolean mDisabledByPolicy;
+ private static int mDefaultMaxTiles;
private int mMaxTiles;
protected QSPanel mFullPanel;
@@ -59,6 +60,7 @@
}
removeView((View) mTileLayout);
}
+ mDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
mTileLayout = new HeaderTileLayout(context);
mTileLayout.setListening(mListening);
addView((View) mTileLayout, 0 /* Between brightness and footer */);
@@ -149,7 +151,7 @@
};
public static int getNumQuickTiles(Context context) {
- return Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, 6);
+ return Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, mDefaultMaxTiles);
}
void setDisabledByPolicy(boolean disabled) {
@@ -190,29 +192,21 @@
mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
R.dimen.qs_quick_tile_size);
-
- setGravity(Gravity.CENTER);
- setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ updateLayoutParams();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ updateLayoutParams();
+ }
+ private void updateLayoutParams() {
setGravity(Gravity.CENTER);
- LayoutParams staticSpaceLayoutParams = generateSpaceLayoutParams(
- mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_tile_space_width));
-
- // Update space params since they fill any open space in portrait orientation and have
- // a static width in landscape orientation.
- final int childViewCount = getChildCount();
- for (int i = 0; i < childViewCount; i++) {
- View childView = getChildAt(i);
- if (childView instanceof Space) {
- childView.setLayoutParams(staticSpaceLayoutParams);
- }
- }
+ int width = getResources().getDimensionPixelSize(R.dimen.qs_quick_layout_width);
+ LayoutParams lp = new LayoutParams(width, LayoutParams.MATCH_PARENT);
+ lp.gravity = Gravity.CENTER_HORIZONTAL;
+ setLayoutParams(lp);
}
/**
@@ -220,11 +214,9 @@
* then we're going to have the space expand to take up as much space as possible. If the
* width is non-zero, we want the inter-tile spacers to be fixed.
*/
- private LayoutParams generateSpaceLayoutParams(int spaceWidth) {
- LayoutParams lp = new LayoutParams(spaceWidth, mTileDimensionSize);
- if (spaceWidth == 0) {
- lp.weight = 1;
- }
+ private LayoutParams generateSpaceLayoutParams() {
+ LayoutParams lp = new LayoutParams(0, mTileDimensionSize);
+ lp.weight = 1;
lp.gravity = Gravity.CENTER;
return lp;
}
@@ -241,13 +233,7 @@
@Override
public void addTile(TileRecord tile) {
if (getChildCount() != 0) {
- // Add a spacer between tiles. We want static-width spaces if we're in landscape to
- // keep the tiles close. For portrait, we stick with spaces that fill up any
- // available space.
- LayoutParams spaceLayoutParams = generateSpaceLayoutParams(
- mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_tile_space_width));
- addView(new Space(mContext), getChildCount(), spaceLayoutParams);
+ addView(new Space(mContext), getChildCount(), generateSpaceLayoutParams());
}
addView(tile.tileView, getChildCount(), generateTileLayoutParams());
@@ -303,6 +289,10 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (hideOverflowingChildren(widthMeasureSpec)) {
+ return; // Rely on visibility change to trigger remeasure.
+ }
+
if (mRecords != null && mRecords.size() > 0) {
View previousView = this;
for (TileRecord record : mRecords) {
@@ -315,5 +305,32 @@
R.id.expand_indicator);
}
}
+
+ /**
+ * Hide child views that would otherwise be clipped.
+ * @return {@code true} if any child visibilities have changed.
+ */
+ private boolean hideOverflowingChildren(int widthMeasureSpec) {
+ if (getChildCount() == 0) {
+ return false;
+ }
+ boolean childVisibilityChanged = false;
+ int widthRemaining = MeasureSpec.getSize(widthMeasureSpec)
+ - getChildAt(0).getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
+ for (int i = 2; i < getChildCount(); i += 2) {
+ View tileChild = getChildAt(i);
+ LayoutParams lp = (LayoutParams) tileChild.getLayoutParams();
+ // All Space views have 0 width; only tiles contribute to the total width.
+ widthRemaining = widthRemaining
+ - tileChild.getMeasuredWidth() - lp.getMarginEnd() - lp.getMarginStart();
+ int newVisibility = widthRemaining < 0 ? View.GONE : View.VISIBLE;
+ if (tileChild.getVisibility() != newVisibility) {
+ tileChild.setVisibility(newVisibility);
+ getChildAt(i - 1).setVisibility(newVisibility); // Hide spacer as well.
+ childVisibilityChanged = true;
+ }
+ }
+ return childVisibilityChanged;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index ebfa6af..91afef02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -265,6 +265,8 @@
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ // Clear selected state so it is not announce by talkback.
+ info.setSelected(false);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 8a0d7e3..64ccba8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -60,6 +60,7 @@
import android.os.UserHandle;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
@@ -246,7 +247,10 @@
try {
// Create screenshot directory if it doesn't exist
- mScreenshotDir.mkdirs();
+ boolean madeDirs = mScreenshotDir.mkdirs();
+ if (madeDirs == false) {
+ Log.e(TAG, "Couldn't create screenshot directory: " + mScreenshotDir);
+ }
// media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds
// for DATE_TAKEN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 135b037..83db6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -37,6 +37,7 @@
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.SystemClock;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -108,6 +109,7 @@
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
private static final int MENU_VIEW_INDEX = 0;
private static final String TAG = "ExpandableNotifRow";
+ public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -157,7 +159,7 @@
private boolean mSensitiveHiddenInGeneral;
private boolean mShowingPublicInitialized;
private boolean mHideSensitiveForIntrinsicHeight;
- private float mHeaderVisibleAmount = 1.0f;
+ private float mHeaderVisibleAmount = DEFAULT_HEADER_VISIBLE_AMOUNT;
/**
* Is this notification expanded by the system. The expansion state can be overridden by the
@@ -992,6 +994,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+ mEntry.setInitializationTime(SystemClock.elapsedRealtime());
Dependency.get(PluginManager.class).addPluginListener(this,
NotificationMenuRowPlugin.class, false /* Allow multiple */);
}
@@ -2851,12 +2854,7 @@
@Override
public Path getCustomClipPath(View child) {
if (child instanceof NotificationGuts) {
- return getClipPath(true, /* ignoreTranslation */
- false /* clipRoundedToBottom */);
- }
- if (child instanceof NotificationChildrenContainer) {
- return getClipPath(false, /* ignoreTranslation */
- true /* clipRoundedToBottom */);
+ return getClipPath(true /* ignoreTranslation */);
}
return super.getCustomClipPath(child);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index edfa61b..584b637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -64,7 +64,6 @@
protected float mOutlineRadius;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- private Path mTmpPath2 = new Path();
private float mCurrentBottomRoundness;
private float mCurrentTopRoundness;
private float mBottomRoundness;
@@ -94,7 +93,7 @@
int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
outline.setRect(left, top, right, bottom);
} else {
- Path clipPath = getClipPath();
+ Path clipPath = getClipPath(false /* ignoreTranslation */);
if (clipPath != null && clipPath.isConvex()) {
// The path might not be convex in border cases where the view is small and
// clipped
@@ -105,36 +104,23 @@
}
};
- private Path getClipPath() {
- return getClipPath(false, /* ignoreTranslation */
- false /* clipRoundedToBottom */);
- }
-
- protected Path getClipPath(boolean ignoreTranslation, boolean clipRoundedToBottom) {
+ protected Path getClipPath(boolean ignoreTranslation) {
int left;
int top;
int right;
int bottom;
int height;
- Path intersectPath = null;
+ float topRoundness = mAlwaysRoundBothCorners
+ ? mOutlineRadius : getCurrentBackgroundRadiusTop();
if (!mCustomOutline) {
int translation = mShouldTranslateContents && !ignoreTranslation
? (int) getTranslation() : 0;
left = Math.max(translation, 0);
top = mClipTopAmount + mBackgroundTop;
right = getWidth() + Math.min(translation, 0);
- bottom = Math.max(getActualHeight(), top);
- int intersectBottom = Math.max(getActualHeight() - mClipBottomAmount, top);
- if (bottom != intersectBottom) {
- if (clipRoundedToBottom) {
- bottom = intersectBottom;
- } else {
- getRoundedRectPath(left, top, right,
- intersectBottom, 0.0f,
- 0.0f, mTmpPath2);
- intersectPath = mTmpPath2;
- }
- }
+ // If the top is rounded we want the bottom to be at most at the top roundness, in order
+ // to avoid the shadow changing when scrolling up.
+ bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness));
} else {
left = mOutlineRect.left;
top = mOutlineRect.top;
@@ -145,8 +131,6 @@
if (height == 0) {
return EMPTY_PATH;
}
- float topRoundness = mAlwaysRoundBothCorners
- ? mOutlineRadius : getCurrentBackgroundRadiusTop();
float bottomRoundness = mAlwaysRoundBothCorners
? mOutlineRadius : getCurrentBackgroundRadiusBottom();
if (topRoundness + bottomRoundness > height) {
@@ -158,11 +142,7 @@
}
getRoundedRectPath(left, top, right, bottom, topRoundness,
bottomRoundness, mTmpPath);
- Path roundedRectPath = mTmpPath;
- if (intersectPath != null) {
- roundedRectPath.op(intersectPath, Path.Op.INTERSECT);
- }
- return roundedRectPath;
+ return mTmpPath;
}
public static void getRoundedRectPath(int left, int top, int right, int bottom,
@@ -219,7 +199,7 @@
if (childNeedsClipping(child)) {
Path clipPath = getCustomClipPath(child);
if (clipPath == null) {
- clipPath = getClipPath();
+ clipPath = getClipPath(false /* ignoreTranslation */);
}
if (clipPath != null) {
if (intersectPath != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 39485c3..ac289d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -21,6 +21,8 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayCutout;
@@ -38,6 +40,12 @@
* The view in the statusBar that contains part of the heads-up information
*/
public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
+ private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
+ "heads_up_status_bar_view_super_parcelable";
+ private static final String FIRST_LAYOUT = "first_layout";
+ private static final String PUBLIC_MODE = "public_mode";
+ private static final String VISIBILITY = "visibility";
+ private static final String ALPHA = "alpha";
private int mAbsoluteStartPadding;
private int mEndMargin;
private View mIconPlaceholder;
@@ -107,6 +115,39 @@
updateMaxWidth();
}
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
+ super.onSaveInstanceState());
+ bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
+ bundle.putBoolean(PUBLIC_MODE, mPublicMode);
+ bundle.putInt(VISIBILITY, getVisibility());
+ bundle.putFloat(ALPHA, getAlpha());
+
+ return bundle;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !(state instanceof Bundle)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ Bundle bundle = (Bundle) state;
+ Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
+ super.onRestoreInstanceState(superState);
+ mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
+ mPublicMode = bundle.getBoolean(PUBLIC_MODE, false);
+ if (bundle.containsKey(VISIBILITY)) {
+ setVisibility(bundle.getInt(VISIBILITY));
+ }
+ if (bundle.containsKey(ALPHA)) {
+ setAlpha(bundle.getFloat(ALPHA));
+ }
+ }
+
@VisibleForTesting
public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
this(context);
@@ -178,11 +219,7 @@
* @param translationX how to translate the horizontal position
*/
public void setPanelTranslation(float translationX) {
- if (isLayoutRtl()) {
- setTranslationX(translationX + mCutOutInset);
- } else {
- setTranslationX(translationX - mCutOutInset);
- }
+ setTranslationX(translationX);
updateDrawingRect();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 35e1511..294e2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +27,7 @@
import android.content.res.Resources;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -70,8 +72,8 @@
private static final boolean DEBUG_CHARGING_SPEED = false;
private static final int MSG_HIDE_TRANSIENT = 1;
- private static final int MSG_CLEAR_FP_MSG = 2;
- private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
+ private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
+ private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
private final Context mContext;
private ViewGroup mIndicationArea;
@@ -461,7 +463,7 @@
public void handleMessage(Message msg) {
if (msg.what == MSG_HIDE_TRANSIENT) {
hideTransientIndication();
- } else if (msg.what == MSG_CLEAR_FP_MSG) {
+ } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
mLockIcon.setTransientFpError(false);
}
}
@@ -526,9 +528,10 @@
}
@Override
- public void onFingerprintHelp(int msgId, String helpString) {
+ public void onBiometricHelp(int msgId, String helpString,
+ BiometricSourceType biometricSourceType) {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+ if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
return;
}
ColorStateList errorColorState = Utils.getColorError(mContext);
@@ -538,10 +541,10 @@
} else if (updateMonitor.isScreenOn()) {
mLockIcon.setTransientFpError(true);
showTransientIndication(helpString, errorColorState);
- hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
- mHandler.removeMessages(MSG_CLEAR_FP_MSG);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
- TRANSIENT_FP_ERROR_TIMEOUT);
+ hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+ mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
+ TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
}
// Help messages indicate that there was actually a try since the last error, so those
// are not two successive error messages anymore.
@@ -549,16 +552,15 @@
}
@Override
- public void onFingerprintError(int msgId, String errString) {
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
- && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
- || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+ if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
return;
}
ColorStateList errorColorState = Utils.getColorError(mContext);
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- // When swiping up right after receiving a fingerprint error, the bouncer calls
+ // When swiping up right after receiving a biometric error, the bouncer calls
// authenticate leading to the same message being shown again on the bouncer.
// We want to avoid this, as it may confuse the user when the message is too
// generic.
@@ -576,6 +578,28 @@
mLastSuccessiveErrorMessage = msgId;
}
+ private boolean shouldSuppressBiometricError(int msgId,
+ BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+ return shouldSuppressFingerprintError(msgId, updateMonitor);
+ if (biometricSourceType == BiometricSourceType.FACE)
+ return shouldSuppressFaceError(msgId, updateMonitor);
+ return false;
+ }
+
+ private boolean shouldSuppressFingerprintError(int msgId,
+ KeyguardUpdateMonitor updateMonitor) {
+ return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ }
+
+ private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
+ return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
+ || msgId == FaceManager.FACE_ERROR_CANCELED);
+ }
+
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
showTransientIndication(message, Utils.getColorError(mContext));
@@ -592,21 +616,22 @@
}
@Override
- public void onFingerprintRunningStateChanged(boolean running) {
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
if (running) {
mMessageToShowOnScreenOn = null;
}
}
@Override
- public void onFingerprintAuthenticated(int userId) {
- super.onFingerprintAuthenticated(userId);
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ super.onBiometricAuthenticated(userId, biometricSourceType);
mLastSuccessiveErrorMessage = -1;
}
@Override
- public void onFingerprintAuthFailed() {
- super.onFingerprintAuthFailed();
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+ super.onBiometricAuthFailed(biometricSourceType);
mLastSuccessiveErrorMessage = -1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 1a645d1..a58752c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -84,6 +84,7 @@
public static final class Entry {
private static final long LAUNCH_COOLDOWN = 2000;
private static final long REMOTE_INPUT_COOLDOWN = 500;
+ private static final long INITIALIZATION_DELAY = 400;
private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
private static final int COLOR_INVALID = 1;
public String key;
@@ -114,6 +115,9 @@
public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
public CharSequence headsUpStatusBarText;
public CharSequence headsUpStatusBarTextPublic;
+
+ private long initializationTime = -1;
+
/**
* Whether or not this row represents a system notification. Note that if this is
* {@code null}, that means we were either unable to retrieve the info or have yet to
@@ -169,6 +173,11 @@
return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
}
+ public boolean hasFinishedInitialization() {
+ return initializationTime == -1 ||
+ SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+ }
+
/**
* Create the icons for a notification
* @param context the context to create the icons with
@@ -341,6 +350,12 @@
}
return false;
}
+
+ public void setInitializationTime(long time) {
+ if (initializationTime == -1) {
+ initializationTime = time;
+ }
+ }
}
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index e52829a..bd40686 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -416,6 +416,12 @@
}
});
mExpandAnimation.start();
+
+ // Since we're swapping/update the content, reset the timeout so the UI can't close
+ // immediately after the update.
+ if (mGutsContainer != null) {
+ mGutsContainer.resetFalsingCheck();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 7e397e8..2f62d59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -109,16 +109,6 @@
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
// Start the overview connection to the launcher service
Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- try {
- final int lastResumedActivityUserId =
- ActivityTaskManager.getService().getLastResumedActivityUserId();
- if (mUserManager.isManagedProfile(lastResumedActivityUserId)) {
- showForegroundManagedProfileActivityToast();
- }
- } catch (RemoteException e) {
- // Abandon hope activity manager not running.
- }
} else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
@@ -225,7 +215,6 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
- filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiver(mBaseBroadcastReceiver, filter);
@@ -238,19 +227,6 @@
mSettingsObserver.onChange(false); // set up
}
- private void showForegroundManagedProfileActivityToast() {
- Toast toast = Toast.makeText(mContext,
- R.string.managed_profile_foreground_toast,
- Toast.LENGTH_SHORT);
- TextView text = toast.getView().findViewById(android.R.id.message);
- text.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
- int paddingPx = mContext.getResources().getDimensionPixelSize(
- R.dimen.managed_profile_toast_padding);
- text.setCompoundDrawablePadding(paddingPx);
- toast.show();
- }
-
public boolean shouldShowLockscreenNotifications() {
return mShowLockscreenNotifications;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUndoLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUndoLayout.java
new file mode 100644
index 0000000..11a1c9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUndoLayout.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+/**
+ * Custom view for the NotificationInfo confirmation views so that the confirmation text can
+ * occupy the full width of the notification and push the undo button down to the next line if
+ * necessary.
+ *
+ * @see NotificationInfo
+ */
+public class NotificationUndoLayout extends FrameLayout {
+ /**
+ * View for the prompt/confirmation text to tell the user the previous action was successful.
+ */
+ private View mConfirmationTextView;
+ /** Undo button (actionable text) view. */
+ private View mUndoView;
+
+ /**
+ * Whether {@link #mConfirmationTextView} is multiline and will require the full width of the
+ * parent (which causes the {@link #mUndoView} to push down).
+ */
+ private boolean mIsMultiline = false;
+ private int mMultilineTopMargin;
+
+ public NotificationUndoLayout(Context context) {
+ this(context, null);
+ }
+
+ public NotificationUndoLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationUndoLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mConfirmationTextView = findViewById(R.id.confirmation_text);
+ mUndoView = findViewById(R.id.undo);
+
+ mMultilineTopMargin = getResources().getDimensionPixelOffset(
+ com.android.internal.R.dimen.notification_content_margin_start);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ LayoutParams confirmationLayoutParams =
+ (LayoutParams) mConfirmationTextView.getLayoutParams();
+ LayoutParams undoLayoutParams =(LayoutParams) mUndoView.getLayoutParams();
+
+ int measuredWidth = getMeasuredWidth();
+ // Ignore the left margin on the undo button - no need for additional extra space between
+ // the text and the button.
+ int requiredWidth = mConfirmationTextView.getMeasuredWidth()
+ + confirmationLayoutParams.rightMargin
+ + confirmationLayoutParams.leftMargin
+ + mUndoView.getMeasuredWidth()
+ + undoLayoutParams.rightMargin;
+ // If the measured width isn't enough to accommodate both the undo button and the text in
+ // the same line, we'll need to adjust the view to be multi-line. Otherwise, we're done.
+ if (requiredWidth > measuredWidth) {
+ mIsMultiline = true;
+
+ // Update height requirement to the text height and the button's height (along with
+ // additional spacing for the top of the text).
+ int updatedHeight = mMultilineTopMargin
+ + mConfirmationTextView.getMeasuredHeight()
+ + mUndoView.getMeasuredHeight()
+ + undoLayoutParams.topMargin
+ + undoLayoutParams.bottomMargin;
+
+ setMeasuredDimension(measuredWidth, updatedHeight);
+ } else {
+ mIsMultiline = false;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ // If the text view and undo view don't fit on the same line, we'll need to manually lay
+ // out the content.
+ if (mIsMultiline) {
+ // Re-align parent right/bottom values. Left and top are considered to be 0.
+ int parentBottom = getMeasuredHeight();
+ int parentRight = getMeasuredWidth();
+
+ LayoutParams confirmationLayoutParams =
+ (LayoutParams) mConfirmationTextView.getLayoutParams();
+ LayoutParams undoLayoutParams = (LayoutParams) mUndoView.getLayoutParams();
+
+ // The confirmation text occupies the full width as computed earlier. Both side margins
+ // are equivalent, so we only need to grab the left one here.
+ mConfirmationTextView.layout(
+ confirmationLayoutParams.leftMargin,
+ mMultilineTopMargin,
+ confirmationLayoutParams.leftMargin + mConfirmationTextView.getMeasuredWidth(),
+ mMultilineTopMargin + mConfirmationTextView.getMeasuredHeight());
+
+ // The undo button is aligned bottom|end with the parent in the case of multiline text.
+ int undoViewLeft = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? undoLayoutParams.rightMargin
+ : parentRight - mUndoView.getMeasuredWidth() - undoLayoutParams.rightMargin;
+ mUndoView.layout(
+ undoViewLeft,
+ parentBottom - mUndoView.getMeasuredHeight() - undoLayoutParams.bottomMargin,
+ undoViewLeft + mUndoView.getMeasuredWidth(),
+ parentBottom - undoLayoutParams.bottomMargin);
+ } else {
+ super.onLayout(changed, left, top, right, bottom);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index ccbf483..6ee3189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -243,7 +243,7 @@
}
@Override
- public void setVisibleState(int state) {
+ public void setVisibleState(int state, boolean animate) {
if (state == mVisibleState) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 0ed6b77..513cd32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -124,7 +124,7 @@
}
@Override
- public void setVisibleState(int state) {
+ public void setVisibleState(int state, boolean animate) {
if (state == mVisibleState) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index b831b86..beb90b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -22,7 +22,10 @@
String getSlot();
void setStaticDrawableColor(int color);
void setDecorColor(int color);
- void setVisibleState(int state);
+ default void setVisibleState(int state) {
+ setVisibleState(state, false);
+ }
+ void setVisibleState(int state, boolean animate);
int getVisibleState();
boolean isIconVisible();
default boolean isIconBlocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 2fc2cdb..ade27f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static com.android.systemui.statusbar.ExpandableNotificationRow
+ .DEFAULT_HEADER_VISIBLE_AMOUNT;
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
import android.app.Notification;
@@ -123,6 +125,9 @@
// Reinspect the notification.
resolveHeaderViews();
+ if (row.getHeaderVisibleAmount() != DEFAULT_HEADER_VISIBLE_AMOUNT) {
+ setHeaderVisibleAmount(row.getHeaderVisibleAmount());
+ }
updateTransformedTypes();
addRemainingTransformTypes();
updateCropToPaddingForImageViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f0b1a82..0b6fd13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
@@ -35,14 +36,14 @@
import java.io.PrintWriter;
/**
- * Controller which coordinates all the fingerprint unlocking actions with the UI.
+ * Controller which coordinates all the biometric unlocking actions with the UI.
*/
-public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
+public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
- private static final String TAG = "FingerprintController";
- private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
- private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
- private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+ private static final String TAG = "BiometricUnlockController";
+ private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK;
+ private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
+ private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
/**
* Mode in which we don't need to wake up the device when we get a fingerprint.
@@ -90,9 +91,9 @@
public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7;
/**
- * How much faster we collapse the lockscreen when authenticating with fingerprint.
+ * How much faster we collapse the lockscreen when authenticating with biometric.
*/
- private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
+ private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
private PowerManager mPowerManager;
private Handler mHandler = new Handler();
@@ -108,15 +109,16 @@
private final UnlockMethodCache mUnlockMethodCache;
private final Context mContext;
private int mPendingAuthenticatedUserId = -1;
+ private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
private boolean mPendingShowBouncer;
private boolean mHasScreenTurnedOnSinceAuthenticating;
- public FingerprintUnlockController(Context context,
- DozeScrimController dozeScrimController,
- KeyguardViewMediator keyguardViewMediator,
- ScrimController scrimController,
- StatusBar statusBar,
- UnlockMethodCache unlockMethodCache) {
+ public BiometricUnlockController(Context context,
+ DozeScrimController dozeScrimController,
+ KeyguardViewMediator keyguardViewMediator,
+ ScrimController scrimController,
+ StatusBar statusBar,
+ UnlockMethodCache unlockMethodCache) {
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
@@ -136,21 +138,21 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
}
- private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
+ private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
@Override
public void run() {
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fp wakelock: TIMEOUT!!");
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "biometric wakelock: TIMEOUT!!");
}
- releaseFingerprintWakeLock();
+ releaseBiometricWakeLock();
}
};
- private void releaseFingerprintWakeLock() {
+ private void releaseBiometricWakeLock() {
if (mWakeLock != null) {
- mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "releasing fp wakelock");
+ mHandler.removeCallbacks(mReleaseBiometricWakeLockRunnable);
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "releasing biometric wakelock");
}
mWakeLock.release();
mWakeLock = null;
@@ -158,24 +160,27 @@
}
@Override
- public void onFingerprintAcquired() {
- Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
- releaseFingerprintWakeLock();
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
+ Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
+ releaseBiometricWakeLock();
if (!mUpdateMonitor.isDeviceInteractive()) {
if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(
- LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
+ int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+ if (biometricSourceType == BiometricSourceType.FACE) {
+ action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
+ }
+ LatencyTracker.getInstance(mContext).onActionStart(action);
}
mWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
+ PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME);
Trace.beginSection("acquiring wake-and-unlock");
mWakeLock.acquire();
Trace.endSection();
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "biometric acquired, grabbing biometric wakelock");
}
- mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
- FINGERPRINT_WAKELOCK_TIMEOUT_MS);
+ mHandler.postDelayed(mReleaseBiometricWakeLockRunnable,
+ BIOMETRIC_WAKELOCK_TIMEOUT_MS);
}
Trace.endSection();
}
@@ -187,10 +192,11 @@
}
@Override
- public void onFingerprintAuthenticated(int userId) {
- Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
mPendingAuthenticatedUserId = userId;
+ mPendingAuthenticatedBioSourceType = biometricSourceType;
Trace.endSection();
return;
}
@@ -211,13 +217,13 @@
mStatusBarWindowManager.setForceDozeBrightness(true);
}
if (!wasDeviceInteractive) {
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fp wakelock: Authenticated, waking up...");
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "bio wakelock: Authenticated, waking up...");
}
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:BIOMETRIC");
}
Trace.beginSection("release wake-and-unlock");
- releaseFingerprintWakeLock();
+ releaseBiometricWakeLock();
Trace.endSection();
switch (mMode) {
case MODE_DISMISS_BOUNCER:
@@ -261,7 +267,7 @@
case MODE_NONE:
break;
}
- mStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyBiometricAuthModeChanged();
Trace.endSection();
}
@@ -270,7 +276,7 @@
mStatusBarKeyguardViewManager.showBouncer(false);
}
mStatusBarKeyguardViewManager.animateCollapsePanels(
- FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
+ BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
mPendingShowBouncer = false;
}
@@ -278,28 +284,31 @@
public void onStartedGoingToSleep(int why) {
resetMode();
mPendingAuthenticatedUserId = -1;
+ mPendingAuthenticatedBioSourceType = null;
}
@Override
public void onFinishedGoingToSleep(int why) {
- Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
+ Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
if (mPendingAuthenticatedUserId != -1) {
// Post this to make sure it's executed after the device is fully locked.
mHandler.post(new Runnable() {
@Override
public void run() {
- onFingerprintAuthenticated(mPendingAuthenticatedUserId);
+ onBiometricAuthenticated(mPendingAuthenticatedUserId,
+ mPendingAuthenticatedBioSourceType);
}
});
}
mPendingAuthenticatedUserId = -1;
+ mPendingAuthenticatedBioSourceType = null;
Trace.endSection();
}
public boolean hasPendingAuthentication() {
return mPendingAuthenticatedUserId != -1
- && mUpdateMonitor.isUnlockingWithFingerprintAllowed()
+ && mUpdateMonitor.isUnlockingWithBiometricAllowed()
&& mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
}
@@ -308,7 +317,7 @@
}
private int calculateMode() {
- boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
+ boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
boolean deviceDreaming = mUpdateMonitor.isDreaming();
if (!mUpdateMonitor.isDeviceInteractive()) {
@@ -338,17 +347,18 @@
}
@Override
- public void onFingerprintAuthFailed() {
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
cleanup();
}
@Override
- public void onFingerprintError(int msgId, String errString) {
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
cleanup();
}
private void cleanup() {
- releaseFingerprintWakeLock();
+ releaseBiometricWakeLock();
}
public void startKeyguardFadingAway() {
@@ -372,7 +382,7 @@
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
}
- mStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyBiometricAuthModeChanged();
}
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
@@ -380,7 +390,7 @@
@Override
public void onFinishedWakingUp() {
if (mPendingShowBouncer) {
- FingerprintUnlockController.this.showBouncer();
+ BiometricUnlockController.this.showBouncer();
}
}
};
@@ -398,7 +408,7 @@
}
public void dump(PrintWriter pw) {
- pw.println(" FingerprintUnlockController:");
+ pw.println(" BiometricUnlockController:");
pw.print(" mMode="); pw.println(mMode);
pw.print(" mWakeLock="); pw.println(mWakeLock);
}
@@ -415,7 +425,7 @@
/**
* Successful authentication with fingerprint when the screen was either on or off.
*/
- public boolean isFingerprintUnlock() {
+ public boolean isBiometricUnlock() {
return isWakeAndUnlock() || mMode == MODE_UNLOCK;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f7b7eeb..ea70ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -22,6 +22,8 @@
import android.app.Fragment;
import android.app.StatusBarManager;
import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -89,7 +91,8 @@
super.onViewCreated(view, savedInstanceState);
mStatusBar = (PhoneStatusBarView) view;
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
- mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
+ mStatusBar.restoreHierarchyState(
+ savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
}
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
mDarkIconManager.setShouldLog(true);
@@ -105,7 +108,9 @@
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putInt(EXTRA_PANEL_STATE, mStatusBar.getState());
+ SparseArray<Parcelable> states = new SparseArray<>();
+ mStatusBar.saveHierarchyState(states);
+ outState.putSparseParcelableArray(EXTRA_PANEL_STATE, states);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 409a783..e1936fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -18,6 +18,7 @@
import android.graphics.Point;
import android.graphics.Rect;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
@@ -53,9 +54,12 @@
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
private final BiConsumer<Float, Float> mSetExpandedHeight = this::setExpandedHeight;
- private float mExpandedHeight;
- private boolean mIsExpanded;
- private float mExpandFraction;
+ @VisibleForTesting
+ float mExpandedHeight;
+ @VisibleForTesting
+ boolean mIsExpanded;
+ @VisibleForTesting
+ float mExpandFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
@@ -99,6 +103,20 @@
mClockView = clockView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mDarkIconDispatcher.addDarkReceiver(this);
+
+ mHeadsUpStatusBarView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (shouldBeVisible()) {
+ updateTopEntry();
+
+ // trigger scroller to notify the latest panel translation
+ mStackScroller.requestLayout();
+ }
+ mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
+ }
+ });
}
@@ -159,8 +177,15 @@
}
WindowInsets windowInset = mStackScroller.getRootWindowInsets();
- return windowInset.getSystemWindowInsetLeft() + mStackScroller.getRight()
- + windowInset.getSystemWindowInsetRight() - realDisplaySize;
+ DisplayCutout cutout = (windowInset != null) ? windowInset.getDisplayCutout() : null;
+ int sysWinLeft = (windowInset != null) ? windowInset.getStableInsetLeft() : 0;
+ int sysWinRight = (windowInset != null) ? windowInset.getStableInsetRight() : 0;
+ int cutoutLeft = (cutout != null) ? cutout.getSafeInsetLeft() : 0;
+ int cutoutRight = (cutout != null) ? cutout.getSafeInsetRight() : 0;
+ int leftInset = Math.max(sysWinLeft, cutoutLeft);
+ int rightInset = Math.max(sysWinRight, cutoutRight);
+
+ return leftInset + mStackScroller.getRight() + rightInset - realDisplaySize;
}
public void updatePanelTranslation() {
@@ -293,4 +318,13 @@
mHeadsUpStatusBarView.setPublicMode(publicMode);
updateTopEntry();
}
+
+ void readFrom(HeadsUpAppearanceController oldController) {
+ if (oldController != null) {
+ mTrackedChild = oldController.mTrackedChild;
+ mExpandedHeight = oldController.mExpandedHeight;
+ mIsExpanded = oldController.mIsExpanded;
+ mExpandFraction = oldController.mExpandFraction;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 46d9827..e2f3319 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -367,10 +367,10 @@
}
}
- private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+ private void startFinishingCircleAnimation(float velocity, Runnable animationEndRunnable,
boolean right) {
KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
- targetView.finishAnimation(velocity, mAnimationEndRunnable);
+ targetView.finishAnimation(velocity, animationEndRunnable);
}
private void setTranslation(float translation, boolean isReset, boolean animateReset) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 3b12051..25b97bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -28,6 +28,7 @@
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -774,7 +775,8 @@
}
@Override
- public void onFingerprintRunningStateChanged(boolean running) {
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
mLockIcon.update();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index e47dcea..20ea27a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -70,6 +70,7 @@
private static final int LAYOUT_CUTOUT = 1;
private static final int LAYOUT_NO_CUTOUT = 2;
+ private boolean mShowPercentAvailable;
private boolean mBatteryCharging;
private boolean mKeyguardUserSwitcherShowing;
private boolean mBatteryListening;
@@ -168,6 +169,8 @@
R.dimen.system_icons_super_container_avatarless_margin_end);
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
+ mShowPercentAvailable = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
}
private void updateVisibilities() {
@@ -189,7 +192,7 @@
mMultiUserSwitch.setVisibility(View.GONE);
}
}
- mBatteryView.setForceShowPercent(mBatteryCharging);
+ mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable);
}
private void updateSystemIconsLayoutParams() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 8b8cbfe..8cace72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -44,7 +44,7 @@
private final DarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
- private FingerprintUnlockController mFingerprintUnlockController;
+ private BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
private int mSystemUiVisibility;
@@ -89,9 +89,9 @@
updateNavigation();
}
- public void setFingerprintUnlockController(
- FingerprintUnlockController fingerprintUnlockController) {
- mFingerprintUnlockController = fingerprintUnlockController;
+ public void setBiometricUnlockController(
+ BiometricUnlockController biometricUnlockController) {
+ mBiometricUnlockController = biometricUnlockController;
}
public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
@@ -178,12 +178,12 @@
}
private boolean animateChange() {
- if (mFingerprintUnlockController == null) {
+ if (mBiometricUnlockController == null) {
return false;
}
- int unlockMode = mFingerprintUnlockController.getMode();
- return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ int unlockMode = mBiometricUnlockController.getMode();
+ return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
}
private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
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 4b66ee5a..8928530 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -306,7 +306,7 @@
private int getState() {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
- boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
+ boolean unlockingAllowed = updateMonitor.isUnlockingWithBiometricAllowed();
if (mTransientFpError) {
return STATE_FINGERPRINT_ERROR;
} else if (mUnlockMethodCache.canSkipBouncer()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index dce7537..ed1ae10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -75,7 +75,6 @@
private int mTouchDownY;
private boolean mDownOnRecents;
private VelocityTracker mVelocityTracker;
- private boolean mIsInScreenPinning;
private boolean mNotificationsVisibleOnDown;
private boolean mDockWindowEnabled;
@@ -110,7 +109,6 @@
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mIsInScreenPinning = mNavigationBarView.inScreenPinning();
mNotificationsVisibleOnDown = !mStatusBar.isPresenterFullyCollapsed();
}
if (!canHandleGestures()) {
@@ -277,8 +275,7 @@
}
private boolean canHandleGestures() {
- return !mIsInScreenPinning && !mStatusBar.isKeyguardShowing()
- && !mNotificationsVisibleOnDown;
+ return !mStatusBar.isKeyguardShowing() && !mNotificationsVisibleOnDown;
}
private int calculateDragMode() {
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 8b9e12c..6077e79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -18,6 +18,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
@@ -161,6 +162,13 @@
private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
+ /**
+ * Helper that is responsible for showing the right toast when a disallowed activity operation
+ * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+ * fully locked mode we only show that unlocking is blocked.
+ */
+ private ScreenPinningNotify mScreenPinningNotify;
+
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -286,6 +294,7 @@
mConfiguration.updateFrom(context.getResources().getConfiguration());
reloadNavIcons();
+ mScreenPinningNotify = new ScreenPinningNotify(mContext);
mBarTransitions = new NavigationBarTransitions(this);
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
@@ -328,15 +337,15 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (shouldDeadZoneConsumeTouchEvents(event)) {
- return true;
- }
+ final boolean deadZoneConsumed = shouldDeadZoneConsumeTouchEvents(event);
switch (event.getActionMasked()) {
case ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
mDownHitTarget = HIT_TARGET_NONE;
- if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
+ if (deadZoneConsumed) {
+ mDownHitTarget = HIT_TARGET_DEAD_ZONE;
+ } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
mDownHitTarget = HIT_TARGET_BACK;
} else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) {
mDownHitTarget = HIT_TARGET_HOME;
@@ -353,9 +362,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (shouldDeadZoneConsumeTouchEvents(event)) {
- return true;
- }
+ shouldDeadZoneConsumeTouchEvents(event);
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
@@ -764,7 +771,7 @@
showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);
}
- private void updateSlippery() {
+ public void updateSlippery() {
setSlippery(!isQuickStepSwipeUpEnabled() || mPanelView.isFullyExpanded());
}
@@ -983,6 +990,18 @@
mBarTransitions.reapplyDarkIntensity();
}
+ public void showPinningEnterExitToast(boolean entering) {
+ if (entering) {
+ mScreenPinningNotify.showPinningStartToast();
+ } else {
+ mScreenPinningNotify.showPinningExitToast();
+ }
+ }
+
+ public void showPinningEscapeToast() {
+ mScreenPinningNotify.showEscapeToast(isRecentsButtonVisible());
+ }
+
public boolean isVertical() {
return mVertical;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index d859b5c..a5b56eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -47,6 +47,8 @@
private final Rect mTintArea = new Rect();
private NotificationStackScrollLayout mNotificationScrollLayout;
private Context mContext;
+ private boolean mFullyDark;
+ private boolean mHasShelfIconsWhenFullyDark;
public NotificationIconAreaController(Context context, StatusBar statusBar) {
mStatusBar = statusBar;
@@ -173,13 +175,40 @@
public void updateNotificationIcons() {
updateStatusBarIcons();
- updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
- NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
- false /* hideRepliedMessages */);
+ updateShelfIcons();
+ updateHasShelfIconsWhenFullyDark();
applyNotificationIconsTint();
}
+ private void updateHasShelfIconsWhenFullyDark() {
+ boolean hasIconsWhenFullyDark = false;
+ for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
+ View view = mNotificationScrollLayout.getChildAt(i);
+ if (view instanceof ExpandableNotificationRow) {
+ NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+ if (shouldShowNotificationIcon(ent,
+ NotificationShelf.SHOW_AMBIENT_ICONS /* showAmbient */,
+ false /* hideDismissed */,
+ true /* hideReplied */)) {
+ hasIconsWhenFullyDark = true;
+ break;
+ }
+ }
+ }
+ mHasShelfIconsWhenFullyDark = hasIconsWhenFullyDark;
+ }
+
+ public boolean hasShelfIconsWhenFullyDark() {
+ return mHasShelfIconsWhenFullyDark;
+ }
+
+ private void updateShelfIcons() {
+ updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+ NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
+ mFullyDark /* hideRepliedMessages */);
+ }
+
public void updateStatusBarIcons() {
updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
@@ -320,6 +349,11 @@
v.setDecorColor(mIconTint);
}
+ public void setFullyDark(boolean fullyDark) {
+ mFullyDark = fullyDark;
+ updateShelfIcons();
+ }
+
public void setDark(boolean dark) {
mNotificationIcons.setDark(dark, false, 0);
mShelfIcons.setDark(dark, false, 0);
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 3ad207a..969b1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2491,7 +2491,7 @@
}
protected void setVerticalPanelTranslation(float translation) {
- mNotificationStackScroller.setTranslationX(translation);
+ mNotificationStackScroller.setVerticalPanelTranslation(translation);
mQsFrame.setTranslationX(translation);
int size = mVerticalTranslationListener.size();
for (int i = 0; i < size; i++) {
@@ -2738,13 +2738,14 @@
public void setPulsing(boolean pulsing) {
mPulsing = pulsing;
- final boolean canAnimatePulse =
- !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
- if (canAnimatePulse) {
+ DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+ final boolean animatePulse = !dozeParameters.getDisplayNeedsBlanking()
+ && dozeParameters.getAlwaysOn();
+ if (animatePulse) {
mAnimateNextPositionUpdate = true;
}
- mNotificationStackScroller.setPulsing(pulsing, canAnimatePulse);
- mKeyguardStatusView.setPulsing(pulsing, canAnimatePulse);
+ mNotificationStackScroller.setPulsing(pulsing, animatePulse);
+ mKeyguardStatusView.setPulsing(pulsing, animatePulse);
}
public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c4d7e72..5d23494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -27,8 +29,11 @@
public static final boolean DEBUG = false;
public static final String TAG = PanelBar.class.getSimpleName();
private static final boolean SPEW = false;
+ private static final String PANEL_BAR_SUPER_PARCELABLE = "panel_bar_super_parcelable";
+ private static final String STATE = "state";
private boolean mBouncerShowing;
private boolean mExpanded;
+ protected float mPanelFraction;
public static final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
@@ -48,8 +53,26 @@
mState = state;
}
- public int getState() {
- return mState;
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(PANEL_BAR_SUPER_PARCELABLE, super.onSaveInstanceState());
+ bundle.putInt(STATE, mState);
+ return bundle;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !(state instanceof Bundle)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ Bundle bundle = (Bundle) state;
+ super.onRestoreInstanceState(bundle.getParcelable(PANEL_BAR_SUPER_PARCELABLE));
+ if (((Bundle) state).containsKey(STATE)) {
+ go(bundle.getInt(STATE, STATE_CLOSED));
+ }
}
public PanelBar(Context context, AttributeSet attrs) {
@@ -77,6 +100,14 @@
if (mPanel != null) mPanel.setImportantForAccessibility(important);
}
+ public float getExpansionFraction() {
+ return mPanelFraction;
+ }
+
+ public boolean isExpanded() {
+ return mExpanded;
+ }
+
private void updateVisibility() {
mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
}
@@ -131,6 +162,7 @@
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
PanelView pv = mPanel;
mExpanded = expanded;
+ mPanelFraction = frac;
updateVisibility();
// adjust any other panels that may be partially visible
if (expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 5477f88..59863ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -59,7 +59,6 @@
private final PhoneStatusBarTransitions mBarTransitions;
private ScrimController mScrimController;
private float mMinFraction;
- private float mPanelFraction;
private Runnable mHideExpandedRunnable = new Runnable() {
@Override
public void run() {
@@ -269,7 +268,6 @@
@Override
public void panelExpansionChanged(float frac, boolean expanded) {
super.panelExpansionChanged(frac, expanded);
- mPanelFraction = frac;
updateScrimFraction();
if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
mBar.getNavigationBarView().onPanelExpandedChange(expanded);
@@ -331,30 +329,25 @@
// or letterboxing from the right or left sides.
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- if (mDisplayCutout == null) {
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+ || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins == null) {
lp.leftMargin = 0;
lp.rightMargin = 0;
return;
}
- lp.leftMargin = mDisplayCutout.getSafeInsetLeft();
- lp.rightMargin = mDisplayCutout.getSafeInsetRight();
+ lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
+ lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
- if (cornerCutoutMargins != null) {
- lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
- lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
-
- // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
- WindowInsets insets = getRootWindowInsets();
- int leftInset = insets.getSystemWindowInsetLeft();
- int rightInset = insets.getSystemWindowInsetRight();
- if (lp.leftMargin <= leftInset) {
- lp.leftMargin = 0;
- }
- if (lp.rightMargin <= rightInset) {
- lp.rightMargin = 0;
- }
-
+ // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
+ WindowInsets insets = getRootWindowInsets();
+ int leftInset = insets.getSystemWindowInsetLeft();
+ int rightInset = insets.getSystemWindowInsetRight();
+ if (lp.leftMargin <= leftInset) {
+ lp.leftMargin = 0;
+ }
+ if (lp.rightMargin <= rightInset) {
+ lp.rightMargin = 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index ea1b980..765cc49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -22,6 +22,7 @@
import static com.android.systemui.Interpolators.ALPHA_OUT;
import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -84,6 +85,7 @@
private AnimatorSet mTrackAnimator;
private ButtonDispatcher mHitTarget;
private View mCurrentNavigationBarView;
+ private boolean mIsInScreenPinning;
private final Handler mHandler = new Handler();
private final Rect mTrackRect = new Rect();
@@ -184,6 +186,8 @@
}
private boolean handleTouchEvent(MotionEvent event) {
+ final boolean deadZoneConsumed =
+ mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
&& !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
return false;
@@ -195,6 +199,7 @@
case MotionEvent.ACTION_DOWN: {
int x = (int) event.getX();
int y = (int) event.getY();
+ mIsInScreenPinning = mNavigationBarView.inScreenPinning();
// End any existing quickscrub animations before starting the new transition
if (mTrackAnimator != null) {
@@ -298,11 +303,11 @@
// Proxy motion events to launcher if not handled by quick scrub
// Proxy motion events up/cancel that would be sent after long press on any nav button
- if (!mQuickScrubActive && (mAllowGestureDetection || action == MotionEvent.ACTION_CANCEL
- || action == MotionEvent.ACTION_UP)) {
+ if (!mQuickScrubActive && !mIsInScreenPinning && (mAllowGestureDetection
+ || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)) {
proxyMotionEvents(event);
}
- return mQuickScrubActive || mQuickStepStarted;
+ return mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
}
@Override
@@ -382,6 +387,12 @@
}
private void startQuickStep(MotionEvent event) {
+ if (mIsInScreenPinning) {
+ mNavigationBarView.showPinningEscapeToast();
+ mAllowGestureDetection = false;
+ return;
+ }
+
mQuickStepStarted = true;
event.transform(mTransformGlobalMatrix);
try {
@@ -407,6 +418,12 @@
}
private void startQuickScrub() {
+ if (mIsInScreenPinning) {
+ mNavigationBarView.showPinningEscapeToast();
+ mAllowGestureDetection = false;
+ return;
+ }
+
if (!mQuickScrubActive) {
mQuickScrubActive = true;
mLightTrackColor = mContext.getColor(R.color.quick_step_track_background_light);
@@ -424,6 +441,9 @@
mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
mTrackAnimator.start();
+ // Disable slippery for quick scrub to not cancel outside the nav bar
+ mNavigationBarView.updateSlippery();
+
try {
mOverviewEventSender.getProxy().onQuickScrubStart();
if (DEBUG_OVERVIEW_PROXY) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index de499d6..fe141661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,9 +29,7 @@
import android.os.Trace;
import android.util.Log;
import android.util.MathUtils;
-import android.view.Choreographer;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -43,12 +41,11 @@
import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.stack.ViewState;
import com.android.systemui.util.AlarmTimeout;
@@ -482,21 +479,13 @@
// Make sure we have the right gradients and their opacities will satisfy GAR.
if (mNeedsDrawableColorUpdate) {
mNeedsDrawableColorUpdate = false;
- final GradientColors currentScrimColors;
- if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER_SCRIMMED
- || mState == ScrimState.BOUNCER) {
- // Always animate color changes if we're seeing the keyguard
- mScrimInFront.setColors(mLockColors, true /* animated */);
- mScrimBehind.setColors(mLockColors, true /* animated */);
- currentScrimColors = mLockColors;
- } else {
- // Only animate scrim color if the scrim view is actually visible
- boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0;
- boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0;
- mScrimInFront.setColors(mSystemColors, animateScrimInFront);
- mScrimBehind.setColors(mSystemColors, animateScrimBehind);
- currentScrimColors = mSystemColors;
- }
+ boolean isKeyguard = mKeyguardUpdateMonitor.isKeyguardVisible() && !mKeyguardOccluded;
+ GradientColors currentScrimColors = isKeyguard ? mLockColors : mSystemColors;
+ // Only animate scrim color if the scrim view is actually visible
+ boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
+ boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
+ mScrimInFront.setColors(currentScrimColors, animateScrimInFront);
+ mScrimBehind.setColors(currentScrimColors, animateScrimBehind);
// Calculate minimum scrim opacity for white or black text.
int textColor = currentScrimColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
@@ -899,6 +888,13 @@
updateScrims();
}
+ public void setHasBackdrop(boolean hasBackdrop) {
+ ScrimState[] states = ScrimState.values();
+ for (int i = 0; i < states.length; i++) {
+ states[i].setHasBackdrop(hasBackdrop);
+ }
+ }
+
public interface Callback {
default void onStart() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 713356b..ec6ada4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -20,7 +20,6 @@
import android.os.Trace;
import android.util.MathUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -106,8 +105,7 @@
public void prepare(ScrimState previousState) {
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
mBlankScreen = mDisplayRequiresBlanking;
- mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
- && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
+ mCurrentBehindAlpha = mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
mCurrentInFrontTint = Color.BLACK;
mCurrentBehindTint = Color.BLACK;
@@ -131,8 +129,7 @@
public void prepare(ScrimState previousState) {
mCurrentInFrontAlpha = 0;
mCurrentInFrontTint = Color.BLACK;
- mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
- && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
+ mCurrentBehindAlpha = mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
mCurrentBehindTint = Color.BLACK;
mBlankScreen = mDisplayRequiresBlanking;
}
@@ -178,8 +175,8 @@
DozeParameters mDozeParameters;
boolean mDisplayRequiresBlanking;
boolean mWallpaperSupportsAmbientMode;
- KeyguardUpdateMonitor mKeyguardUpdateMonitor;
int mIndex;
+ boolean mHasBackdrop;
ScrimState(int index) {
mIndex = index;
@@ -190,7 +187,6 @@
mScrimBehind = scrimBehind;
mDozeParameters = dozeParameters;
mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
- mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
}
public void prepare(ScrimState previousState) {
@@ -256,4 +252,8 @@
public boolean isLowPowerState() {
return false;
}
+
+ public void setHasBackdrop(boolean hasBackdrop) {
+ mHasBackdrop = hasBackdrop;
+ }
}
\ No newline at end of file
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 a80673f..d8e3dd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -361,7 +361,7 @@
private VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mBrightnessMirrorVisible;
- protected FingerprintUnlockController mFingerprintUnlockController;
+ protected BiometricUnlockController mBiometricUnlockController;
private LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
@@ -423,13 +423,6 @@
protected KeyguardViewMediator mKeyguardViewMediator;
private ZenModeController mZenController;
- /**
- * Helper that is responsible for showing the right toast when a disallowed activity operation
- * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
- * fully locked mode we only show that unlocking is blocked.
- */
- private ScreenPinningNotify mScreenPinningNotify;
-
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -512,7 +505,7 @@
protected NotificationLockscreenUserManager mLockscreenUserManager;
protected NotificationRemoteInputManager mRemoteInputManager;
- private BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -520,7 +513,7 @@
Log.w(TAG, "WallpaperManager not available");
return;
}
- WallpaperInfo info = wallpaperManager.getWallpaperInfo();
+ WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean supportsAmbientMode = info != null &&
info.getSupportsAmbientMode();
@@ -714,7 +707,8 @@
// Make sure we always have the most current wallpaper info.
IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
+ mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
+ wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
mWallpaperChangedReceiver.onReceive(mContext, null);
mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
@@ -832,23 +826,45 @@
.createNotificationIconAreaController(context, this);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
+ mStackScroller.setIconAreaController(mNotificationIconAreaController);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
+ PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
+
+ // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
+ // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.
+ // PhoneStatusBarView's new instance will set to be gone in
+ // PanelBar.updateVisibility after calling mStatusBarView.setBouncerShowing
+ // that will trigger PanelBar.updateVisibility. If there is a heads up showing,
+ // it needs to notify PhoneStatusBarView's new instance to update the correct
+ // status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
+ if (mHeadsUpManager.hasPinnedHeadsUp()) {
+ mNotificationPanel.notifyBarPanelExpansionChanged();
+ }
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ if (oldStatusBarView != null) {
+ float fraction = oldStatusBarView.getExpansionFraction();
+ boolean expanded = oldStatusBarView.isExpanded();
+ mStatusBarView.panelExpansionChanged(fraction, expanded);
+ }
+
+ HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
if (mHeadsUpAppearanceController != null) {
// This view is being recreated, let's destroy the old one
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
+ mHeadsUpAppearanceController.readFrom(oldController);
+ mStatusBarWindow.setStatusBarView(mStatusBarView);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
@@ -886,7 +902,6 @@
} catch (RemoteException ex) {
// no window manager? good luck with that
}
- mScreenPinningNotify = new ScreenPinningNotify(mContext);
mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
@@ -1314,18 +1329,18 @@
protected void startKeyguard() {
Trace.beginSection("StatusBar#startKeyguard");
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
- mFingerprintUnlockController = new FingerprintUnlockController(mContext,
+ mBiometricUnlockController = new BiometricUnlockController(mContext,
mDozeScrimController, keyguardViewMediator,
mScrimController, this, UnlockMethodCache.getInstance(mContext));
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
- getBouncerContainer(), mNotificationPanel, mFingerprintUnlockController);
+ getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+ mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
- mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+ mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked);
Trace.endSection();
}
@@ -1609,8 +1624,8 @@
return; // called too early
}
- boolean wakeAndUnlock = mFingerprintUnlockController != null
- && mFingerprintUnlockController.isWakeAndUnlock();
+ boolean wakeAndUnlock = mBiometricUnlockController != null
+ && mBiometricUnlockController.isWakeAndUnlock();
if (mLaunchTransitionFadingAway || wakeAndUnlock) {
mBackdrop.setVisibility(View.INVISIBLE);
Trace.endSection();
@@ -1655,11 +1670,15 @@
&& mStatusBarKeyguardViewManager.isOccluded();
final boolean hasArtwork = artworkDrawable != null;
+ mColorExtractor.setHasBackdrop(hasArtwork);
+ if (mScrimController != null) {
+ mScrimController.setHasBackdrop(hasArtwork);
+ }
- if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && !mDozing
+ if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
&& (mState != StatusBarState.SHADE || allowWhenShade)
- && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && mBiometricUnlockController.getMode()
+ != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
&& !hideBecauseOccluded) {
// time to show some art!
if (mBackdrop.getVisibility() != View.VISIBLE) {
@@ -1672,7 +1691,6 @@
mBackdrop.setAlpha(1f);
}
mStatusBarWindowManager.setBackdropShowing(true);
- mColorExtractor.setMediaBackdropVisible(true);
metaDataChanged = true;
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
@@ -1724,10 +1742,9 @@
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
- mColorExtractor.setMediaBackdropVisible(false);
boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
- if (mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ if (mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
|| hideBecauseOccluded || cannotAnimateDoze) {
// We are unlocking directly - no animation!
@@ -2223,17 +2240,16 @@
@Override
public void showPinningEnterExitToast(boolean entering) {
- if (entering) {
- mScreenPinningNotify.showPinningStartToast();
- } else {
- mScreenPinningNotify.showPinningExitToast();
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().showPinningEnterExitToast(entering);
}
}
@Override
public void showPinningEscapeToast() {
- mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null
- || getNavigationBarView().isRecentsButtonVisible());
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().showPinningEscapeToast();
+ }
}
boolean panelsEnabled() {
@@ -2460,8 +2476,8 @@
return mGestureRec;
}
- public FingerprintUnlockController getFingerprintUnlockController() {
- return mFingerprintUnlockController;
+ public BiometricUnlockController getBiometricUnlockController() {
+ return mBiometricUnlockController;
}
@Override // CommandQueue
@@ -2795,8 +2811,8 @@
DozeLog.dump(pw);
- if (mFingerprintUnlockController != null) {
- mFingerprintUnlockController.dump(pw);
+ if (mBiometricUnlockController != null) {
+ mBiometricUnlockController.dump(pw);
}
if (mKeyguardIndicationController != null) {
@@ -3104,10 +3120,10 @@
&& mUnlockMethodCache.canSkipBouncer()
&& !mLeaveOpenOnKeyguardHide
&& isPulsing()) {
- // Reuse the fingerprint wake-and-unlock transition if we dismiss keyguard from a pulse.
- // TODO: Factor this transition out of FingerprintUnlockController.
- mFingerprintUnlockController.startWakeAndUnlock(
- FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
+ // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
+ // TODO: Factor this transition out of BiometricUnlockController.
+ mBiometricUnlockController.startWakeAndUnlock(
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
if (mStatusBarKeyguardViewManager.isShowing()) {
mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
@@ -3147,6 +3163,7 @@
updateNotificationViews();
mMediaManager.clearCurrentMediaNotification();
setLockscreenUser(newUserId);
+ mWallpaperChangedReceiver.onReceive(mContext, null);
}
@Override
@@ -3511,8 +3528,8 @@
}
private boolean updateIsKeyguard() {
- boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
// For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
// there's no surface we can show to the user. Note that the device goes fully interactive
@@ -3559,8 +3576,8 @@
}
private void updatePanelExpansionForKeyguard() {
- if (mState == StatusBarState.KEYGUARD && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
+ != BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
instantExpandNotificationsPanel();
} else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
instantCollapseNotificationPanel();
@@ -3856,7 +3873,7 @@
* Switches theme from light to dark and vice-versa.
*/
protected void updateTheme() {
- final boolean inflated = mStackScroller != null;
+ final boolean inflated = mStackScroller != null && mStatusBarWindowManager != null;
// The system wallpaper defines if QS should be light or dark.
WallpaperColors systemColors = mColorExtractor
@@ -4688,7 +4705,7 @@
|| mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
}
- public void notifyFpAuthModeChanged() {
+ public void notifyBiometricAuthModeChanged() {
updateDozing();
updateScrimController();
}
@@ -4697,13 +4714,13 @@
Trace.beginSection("StatusBar#updateDozing");
// When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
- || mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ || mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
final boolean alwaysOn = DozeParameters.getInstance(mContext).getAlwaysOn();
// When in wake-and-unlock we may not have received a change to mState
// but we still should not be dozing, manually set to false.
- if (mFingerprintUnlockController.getMode() ==
- FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ if (mBiometricUnlockController.getMode() ==
+ mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
dozing = false;
}
if (mDozing != dozing) {
@@ -4727,20 +4744,18 @@
// We don't want to end up in KEYGUARD state when we're unlocking with
// fingerprint from doze. We should cross fade directly from black.
- boolean wakeAndUnlocking = mFingerprintUnlockController.isWakeAndUnlock();
+ boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock();
// Do not animate the scrim expansion when triggered by the fingerprint sensor.
mScrimController.setExpansionAffectsAlpha(
- !mFingerprintUnlockController.isFingerprintUnlock());
+ !mBiometricUnlockController.isBiometricUnlock());
if (mBouncerShowing) {
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
- ScrimState state = mIsOccluded || mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
- || mStatusBarKeyguardViewManager.willDismissWithAction()
- || mStatusBarKeyguardViewManager.isFullscreenBouncer() ?
- ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+ ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
+ ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
} else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
@@ -4875,8 +4890,8 @@
@Override
public boolean isPulsingBlocked() {
- return mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ return mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
}
@Override
@@ -4887,7 +4902,7 @@
@Override
public boolean isBlockingDoze() {
- if (mFingerprintUnlockController.hasPendingAuthentication()) {
+ if (mBiometricUnlockController.hasPendingAuthentication()) {
Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 914bba2..378910a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.phone;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -91,7 +91,7 @@
protected ViewMediatorCallback mViewMediatorCallback;
protected StatusBar mStatusBar;
private NotificationPanelView mNotificationPanelView;
- private FingerprintUnlockController mFingerprintUnlockController;
+ private BiometricUnlockController mBiometricUnlockController;
private ViewGroup mContainer;
@@ -108,7 +108,7 @@
private boolean mLastBouncerDismissible;
protected boolean mLastRemoteInputActive;
private boolean mLastDozing;
- private int mLastFpMode;
+ private int mLastBiometricMode;
private boolean mGoingToSleepVisibleNotOccluded;
private OnDismissAction mAfterKeyguardGoneAction;
@@ -142,11 +142,11 @@
public void registerStatusBar(StatusBar statusBar,
ViewGroup container,
NotificationPanelView notificationPanelView,
- FingerprintUnlockController fingerprintUnlockController,
+ BiometricUnlockController biometricUnlockController,
DismissCallbackRegistry dismissCallbackRegistry) {
mStatusBar = statusBar;
mContainer = container;
- mFingerprintUnlockController = fingerprintUnlockController;
+ mBiometricUnlockController = biometricUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
mExpansionCallback);
@@ -170,8 +170,7 @@
// • Full-screen user switcher is displayed.
if (mNotificationPanelView.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
- } else if (mOccluded || mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed()
- || mStatusBar.isFullScreenUserSwitcherState()) {
+ } else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !mDozing) {
if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
@@ -260,7 +259,7 @@
}
private boolean isWakeAndUnlocking() {
- int mode = mFingerprintUnlockController.getMode();
+ int mode = mBiometricUnlockController.getMode();
return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING;
}
@@ -443,13 +442,13 @@
} else {
executeAfterKeyguardGoneAction();
boolean wakeUnlockPulsing =
- mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
+ mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
if (wakeUnlockPulsing) {
delay = 0;
fadeoutDuration = 240;
}
mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
- mFingerprintUnlockController.startKeyguardFadingAway();
+ mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
mStatusBar.fadeKeyguardWhilePulsing();
@@ -461,7 +460,7 @@
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
- mFingerprintUnlockController.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
}
}
updateStates();
@@ -485,14 +484,14 @@
mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
100);
mStatusBar.finishKeyguardFadingAway();
- mFingerprintUnlockController.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
private void wakeAndUnlockDejank() {
- if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
+ if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
&& LatencyTracker.isEnabled(mContext)) {
DejankUtils.postAfterTraversal(() ->
LatencyTracker.getInstance(mContext).onActionEnd(
@@ -619,7 +618,7 @@
mLastBouncerDismissible = bouncerDismissible;
mLastRemoteInputActive = remoteInputActive;
mLastDozing = mDozing;
- mLastFpMode = mFingerprintUnlockController.getMode();
+ mLastBiometricMode = mBiometricUnlockController.getMode();
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
@@ -644,9 +643,9 @@
* @return Whether the navigation bar should be made visible based on the current state.
*/
protected boolean isNavBarVisible() {
- int fpMode = mFingerprintUnlockController.getMode();
+ int biometricMode = mBiometricUnlockController.getMode();
boolean keyguardShowing = mShowing && !mOccluded;
- boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+ boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
|| mRemoteInputActive);
}
@@ -656,7 +655,7 @@
*/
protected boolean getLastNavBarVisible() {
boolean keyguardShowing = mLastShowing && !mLastOccluded;
- boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+ boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|| mLastRemoteInputActive);
}
@@ -731,12 +730,9 @@
}
}
- public boolean willDismissWithAction() {
- return mBouncer.willDismissWithAction();
- }
-
public boolean bouncerNeedsScrimming() {
- return mBouncer.isShowingScrimmed();
+ return mOccluded || mBouncer.willDismissWithAction() || mBouncer.needsFullscreenBouncer()
+ || mStatusBar.isFullScreenUserSwitcherState() || mBouncer.isShowingScrimmed();
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 0390f60..fa763c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -37,6 +37,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.ActionMode;
+import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.InputQueue;
import android.view.KeyEvent;
@@ -75,6 +76,7 @@
private NotificationStackScrollLayout mStackScrollLayout;
private NotificationPanelView mNotificationPanel;
private View mBrightnessMirror;
+ private PhoneStatusBarView mStatusBarView;
private int mRightInset = 0;
private int mLeftInset = 0;
@@ -118,10 +120,21 @@
boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
+ int rightCutout = 0;
+ int leftCutout = 0;
+ DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+ if (displayCutout != null) {
+ leftCutout = displayCutout.getSafeInsetLeft();
+ rightCutout = displayCutout.getSafeInsetRight();
+ }
+
+ int targetLeft = Math.max(insets.left, leftCutout);
+ int targetRight = Math.max(insets.right, rightCutout);
+
// Super-special right inset handling, because scrims and backdrop need to ignore it.
- if (insets.right != mRightInset || insets.left != mLeftInset) {
- mRightInset = insets.right;
- mLeftInset = insets.left;
+ if (targetRight != mRightInset || targetLeft != mLeftInset) {
+ mRightInset = targetRight;
+ mLeftInset = targetLeft;
applyMargins();
}
// Drop top inset, and pass through bottom inset.
@@ -192,6 +205,10 @@
}
}
+ public void setStatusBarView(PhoneStatusBarView statusBarView) {
+ mStatusBarView = statusBarView;
+ }
+
public void setService(StatusBar service) {
mService = service;
setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout, mService));
@@ -314,7 +331,7 @@
expandingBelowNotch = true;
}
if (expandingBelowNotch) {
- return mNotificationPanel.dispatchTouchEvent(ev);
+ return mStatusBarView.dispatchTouchEvent(ev);
}
return super.dispatchTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 0811179..f8f6981 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -301,11 +301,7 @@
vs.initFrom(child);
vs.alpha = 1.0f;
- if (child instanceof StatusIconDisplayable) {
- vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
- } else {
- vs.hidden = false;
- }
+ vs.hidden = false;
}
}
@@ -333,22 +329,33 @@
}
StatusIconDisplayable icon = (StatusIconDisplayable) view;
AnimationProperties animationProperties = null;
- boolean animate = false;
+ boolean animateVisibility = true;
- if (justAdded) {
+ // Figure out which properties of the state transition (if any) we need to animate
+ if (justAdded
+ || icon.getVisibleState() == STATE_HIDDEN && visibleState == STATE_ICON) {
+ // Icon is appearing, fade it in by putting it where it will be and animating alpha
super.applyToView(view);
+ view.setAlpha(0.f);
+ icon.setVisibleState(STATE_HIDDEN);
animationProperties = ADD_ICON_PROPERTIES;
- animate = true;
} else if (icon.getVisibleState() != visibleState) {
- animationProperties = DOT_ANIMATION_PROPERTIES;
- animate = true;
+ if (icon.getVisibleState() == STATE_ICON && visibleState == STATE_HIDDEN) {
+ // Disappearing, don't do anything fancy
+ animateVisibility = false;
+ } else {
+ // all other transitions (to/from dot, etc)
+ animationProperties = ANIMATE_ALL_PROPERTIES;
+ }
+ } else if (visibleState != STATE_HIDDEN && xTranslation != view.getTranslationX()) {
+ // Visibility isn't changing, just animate position
+ animationProperties = X_ANIMATION_PROPERTIES;
}
- if (animate) {
+ icon.setVisibleState(visibleState, animateVisibility);
+ if (animationProperties != null) {
animateTo(view, animationProperties);
- icon.setVisibleState(visibleState);
} else {
- icon.setVisibleState(visibleState);
super.applyToView(view);
}
@@ -365,7 +372,7 @@
}
}.setDuration(200).setDelay(50);
- private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
+ private static final AnimationProperties X_ANIMATION_PROPERTIES = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
@Override
@@ -373,4 +380,14 @@
return mAnimationFilter;
}
}.setDuration(200);
+
+ private static final AnimationProperties ANIMATE_ALL_PROPERTIES = new AnimationProperties() {
+ private AnimationFilter mAnimationFilter = new AnimationFilter().animateX().animateY()
+ .animateAlpha().animateScale();
+
+ @Override
+ public AnimationFilter getAnimationFilter() {
+ return mAnimationFilter;
+ }
+ }.setDuration(200);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index f9c2130..e5925f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.Context;
import android.os.Trace;
@@ -135,9 +136,9 @@
}
@Override
- public void onFingerprintAuthenticated(int userId) {
- Trace.beginSection("KeyguardUpdateMonitorCallback#onFingerprintAuthenticated");
- if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
+ if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
Trace.endSection();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9aa8044..8517d90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -25,6 +25,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.Spannable;
@@ -65,6 +66,12 @@
DarkReceiver, ConfigurationListener {
public static final String CLOCK_SECONDS = "clock_seconds";
+ private static final String CLOCK_SUPER_PARCELABLE = "clock_super_parcelable";
+ private static final String CURRENT_USER_ID = "current_user_id";
+ private static final String VISIBLE_BY_POLICY = "visible_by_policy";
+ private static final String VISIBLE_BY_USER = "visible_by_user";
+ private static final String SHOW_SECONDS = "show_seconds";
+ private static final String VISIBILITY = "visibility";
private final CurrentUserTracker mCurrentUserTracker;
private int mCurrentUserId;
@@ -129,6 +136,40 @@
}
@Override
+ public Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(CLOCK_SUPER_PARCELABLE, super.onSaveInstanceState());
+ bundle.putInt(CURRENT_USER_ID, mCurrentUserId);
+ bundle.putBoolean(VISIBLE_BY_POLICY, mClockVisibleByPolicy);
+ bundle.putBoolean(VISIBLE_BY_USER, mClockVisibleByUser);
+ bundle.putBoolean(SHOW_SECONDS, mShowSeconds);
+ bundle.putInt(VISIBILITY, getVisibility());
+
+ return bundle;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !(state instanceof Bundle)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ Bundle bundle = (Bundle) state;
+ Parcelable superState = bundle.getParcelable(CLOCK_SUPER_PARCELABLE);
+ super.onRestoreInstanceState(superState);
+ if (bundle.containsKey(CURRENT_USER_ID)) {
+ mCurrentUserId = bundle.getInt(CURRENT_USER_ID);
+ }
+ mClockVisibleByPolicy = bundle.getBoolean(VISIBLE_BY_POLICY, true);
+ mClockVisibleByUser = bundle.getBoolean(VISIBLE_BY_USER, true);
+ mShowSeconds = bundle.getBoolean(SHOW_SECONDS, false);
+ if (bundle.containsKey(VISIBILITY)) {
+ setVisibility(bundle.getInt(VISIBILITY));
+ }
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index d6d0673..3c16329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -134,6 +134,10 @@
@Override
public void setHotspotEnabled(boolean enabled) {
+ if (mWaitingForCallback) {
+ if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for callback.");
+ return;
+ }
if (enabled) {
OnStartTetheringCallback callback = new OnStartTetheringCallback();
mWaitingForCallback = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 2031b27..59b376f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -113,10 +113,6 @@
@Override
public void addCallback(Callback callback) {
- if (callback == null) {
- Slog.e(TAG, "Attempted to add a null callback.");
- return;
- }
mCallbacks.add(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index a8464b3..fd49b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -137,6 +137,10 @@
// to look nice
customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
+ StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
+ } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+ .ANIMATION_TYPE_PULSE_APPEAR || ev.animationType ==
+ NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
+ customDelay = StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 2dd3d4e..ffd5494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -49,10 +49,14 @@
*/
public class NotificationChildrenContainer extends ViewGroup {
- private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
- private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
- private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
- private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
@@ -699,15 +703,18 @@
return childState.height != intrinsicHeight && !childState.hidden;
}
- private int getMaxAllowedVisibleChildren() {
+ @VisibleForTesting
+ int getMaxAllowedVisibleChildren() {
return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
}
- private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
+ @VisibleForTesting
+ int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
if (mContainingNotification.isShowingAmbient()) {
return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
}
- if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) {
+ if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
+ && !showingAsLowPriority()) {
return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
}
if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c22bd5c..4264adad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -101,6 +101,7 @@
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -420,6 +421,8 @@
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private NotificationIconAreaController mIconAreaController;
+ private float mVerticalPanelTranslation;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -536,10 +539,16 @@
final int lockScreenRight = getWidth() - mSidePaddings;
final int lockScreenTop = mCurrentBounds.top;
final int lockScreenBottom = mCurrentBounds.bottom;
- final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
- final int darkRight = darkLeft + mSeparatorWidth;
- final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
- final int darkBottom = darkTop + mSeparatorThickness;
+ int separatorWidth = 0;
+ int separatorThickness = 0;
+ if (mIconAreaController.hasShelfIconsWhenFullyDark()) {
+ separatorThickness = mSeparatorThickness;
+ separatorWidth = mSeparatorWidth;
+ }
+ final int darkLeft = getWidth() / 2 - separatorWidth / 2;
+ final int darkRight = darkLeft + separatorWidth;
+ final int darkTop = (int) (mRegularTopPadding + separatorThickness / 2f);
+ final int darkBottom = darkTop + separatorThickness;
if (mAmbientState.hasPulsingNotifications()) {
// No divider, we have a notification icon instead
@@ -974,7 +983,7 @@
* Measured relative to the resting position.
*/
private float getExpandTranslationStart() {
- return - mTopPadding;
+ return -mTopPadding + getMinExpansionHeight();
}
/**
@@ -2045,11 +2054,9 @@
}
private int getScrollRange() {
- int contentHeight = getContentHeight();
- int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
+ int scrollRange = Math.max(0, mContentHeight - mMaxLayoutHeight);
int imeInset = getImeInset();
- scrollRange += Math.min(imeInset, Math.max(0,
- getContentHeight() - (getHeight() - imeInset)));
+ scrollRange += Math.min(imeInset, Math.max(0, mContentHeight - (getHeight() - imeInset)));
return scrollRange;
}
@@ -2150,10 +2157,6 @@
return count;
}
- public int getContentHeight() {
- return mContentHeight;
- }
-
private void updateContentHeight() {
int height = 0;
float previousPaddingRequest = mPaddingBetweenElements;
@@ -2217,7 +2220,11 @@
}
}
mIntrinsicContentHeight = height;
- mContentHeight = height + mTopPadding + mBottomMargin;
+
+ // We don't want to use the toppadding since that might be interpolated and we want
+ // to take the final value of the animation.
+ int topPadding = mAmbientState.isFullyDark() ? mDarkTopPadding : mRegularTopPadding;
+ mContentHeight = height + topPadding + mBottomMargin;
updateScrollability();
clampScrollPosition();
mAmbientState.setLayoutMaxHeight(mContentHeight);
@@ -3997,8 +4004,13 @@
notifyHeightChangeListener(mShelf);
}
- private void updateAntiBurnInTranslation() {
- setTranslationX(mAntiBurnInOffsetX * mDarkAmount);
+ private void updatePanelTranslation() {
+ setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mDarkAmount);
+ }
+
+ public void setVerticalPanelTranslation(float verticalPanelTranslation) {
+ mVerticalPanelTranslation = verticalPanelTranslation;
+ updatePanelTranslation();
}
/**
@@ -4015,16 +4027,20 @@
mDarkAmount = darkAmount;
boolean wasFullyDark = mAmbientState.isFullyDark();
mAmbientState.setDarkAmount(darkAmount);
- if (mAmbientState.isFullyDark() != wasFullyDark) {
+ boolean nowFullyDark = mAmbientState.isFullyDark();
+ if (nowFullyDark != wasFullyDark) {
updateContentHeight();
DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
- if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) {
+ if (nowFullyDark && dozeParameters.shouldControlScreenOff()) {
mShelf.fadeInTranslating();
}
+ if (mIconAreaController != null) {
+ mIconAreaController.setFullyDark(nowFullyDark);
+ }
}
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
- updateAntiBurnInTranslation();
+ updatePanelTranslation();
requestChildrenUpdate();
}
@@ -4592,7 +4608,7 @@
public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
mAntiBurnInOffsetX = antiBurnInOffsetX;
- updateAntiBurnInTranslation();
+ updatePanelTranslation();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -4638,6 +4654,10 @@
mHeadsUpAppearanceController = headsUpAppearanceController;
}
+ public void setIconAreaController(NotificationIconAreaController controller) {
+ mIconAreaController = controller;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@@ -4701,10 +4721,13 @@
if (currView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
- mCurrMenuRow = row.createMenu();
- mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
- mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
- mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
+
+ if (row.getEntry().hasFinishedInitialization()) {
+ mCurrMenuRow = row.createMenu();
+ mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
+ mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
+ mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
+ }
}
}
@@ -5051,11 +5074,13 @@
// ANIMATION_TYPE_PULSE_APPEAR
new AnimationFilter()
.animateAlpha()
+ .hasDelays()
.animateY(),
// ANIMATION_TYPE_PULSE_DISAPPEAR
new AnimationFilter()
.animateAlpha()
+ .hasDelays()
.animateY(),
};
@@ -5119,10 +5144,10 @@
StackStateAnimator.ANIMATION_DURATION_STANDARD,
// ANIMATION_TYPE_PULSE_APPEAR
- KeyguardSliceView.DEFAULT_ANIM_DURATION,
+ StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR,
// ANIMATION_TYPE_PULSE_DISAPPEAR
- KeyguardSliceView.DEFAULT_ANIM_DURATION / 2,
+ StackStateAnimator.ANIMATION_DURATION_PULSE_APPEAR / 2,
};
static final int ANIMATION_TYPE_ADD = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 0d50f5a..ee006d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -178,7 +178,7 @@
return false;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (row.areGutsExposed()) {
+ if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
return row.canViewBeDismissed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index a75d40f..b83a09d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -24,6 +24,7 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -51,6 +52,8 @@
= (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
* HeadsUpAppearInterpolator.getFractionUntilOvershoot());
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
+ public static final int ANIMATION_DURATION_PULSE_APPEAR =
+ KeyguardSliceView.DEFAULT_ANIM_DURATION;
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
@@ -430,15 +433,26 @@
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
ExpandableViewState viewState = finalState.getViewStateForView(changingView);
- mTmpState.copyFrom(viewState);
- mTmpState.yTranslation += mPulsingAppearingTranslation;
- mTmpState.alpha = 0;
- mTmpState.applyToView(changingView);
+ if (viewState != null) {
+ mTmpState.copyFrom(viewState);
+ mTmpState.yTranslation += mPulsingAppearingTranslation;
+ mTmpState.alpha = 0;
+ mTmpState.applyToView(changingView);
+ }
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
ExpandableViewState viewState = finalState.getViewStateForView(changingView);
- viewState.yTranslation += mPulsingAppearingTranslation;
- viewState.alpha = 0;
+ if (viewState != null) {
+ viewState.alpha = 0;
+ // We want to animate the alpha away before the view starts translating,
+ // otherwise everything will overlap and look xtra ugly.
+ float originalYTranslation = viewState.yTranslation;
+ viewState.yTranslation = changingView.getTranslationY();
+ mAnimationFilter.animateAlpha = true;
+ mAnimationProperties.duration = ANIMATION_DURATION_PULSE_APPEAR / 2;
+ viewState.animateTo(changingView, mAnimationProperties);
+ viewState.yTranslation = originalYTranslation;
+ }
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
// This item is added, initialize it's properties.
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 67fa049..c468fef 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -113,7 +113,7 @@
NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP,
name, NotificationManager.IMPORTANCE_HIGH); // pop on screen
- screenshotChannel.setSound(Uri.parse(""), // silent
+ screenshotChannel.setSound(null, // silent
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
screenshotChannel.setBlockableSystem(true);
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index eca6127..6812410 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -26,11 +26,14 @@
/**
* Allows lambda iteration over a list. It is done in reverse order so it is safe
- * to add or remove items during the iteration.
+ * to add or remove items during the iteration. Skips over null items.
*/
public static <T> void safeForeach(List<T> list, Consumer<T> c) {
for (int i = list.size() - 1; i >= 0; i--) {
- c.accept(list.get(i));
+ T item = list.get(i);
+ if (item != null) {
+ c.accept(item);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d7fad67..4a9856b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -193,11 +193,11 @@
}
public void show(int reason) {
- mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+ mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
}
public void dismiss(int reason) {
- mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+ mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
}
private void showH(int reason) {
@@ -223,7 +223,7 @@
mHandler.removeMessages(H.DISMISS);
final int timeout = computeTimeoutH();
mHandler.sendMessageDelayed(mHandler
- .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
if (D.BUG) {
Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
@@ -246,7 +246,6 @@
}
mListView.animate().cancel();
- mShowing = false;
mListView.setTranslationY(0);
mListView.setAlpha(1);
@@ -260,6 +259,7 @@
Log.d(TAG, "mDialog.dismiss()");
}
mDialog.dismiss();
+ mShowing = false;
}, DISMISS_DELAY_IN_MILLIS))
.start();
@@ -436,7 +436,8 @@
public boolean onTouchEvent(MotionEvent event) {
if (isShowing()) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+ mHandler.obtainMessage(
+ H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
return true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 8153953..13dc36d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -93,7 +93,7 @@
SysuiColorExtractor extractor = getTestableExtractor(colors);
simulateEvent(extractor);
extractor.setWallpaperVisible(true);
- extractor.setMediaBackdropVisible(true);
+ extractor.setHasBackdrop(true);
ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index ff12c53..f363cf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -308,4 +308,12 @@
mGroupRow.resetTranslation();
assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
}
+
+ @Test
+ public void testIsExpanded_userExpanded() {
+ mGroupRow.setExpandable(true);
+ Assert.assertFalse(mGroupRow.isExpanded());
+ mGroupRow.setUserExpanded(true);
+ Assert.assertTrue(mGroupRow.isExpanded());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 537bfd4..fe7bf25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -123,6 +123,27 @@
}
@Test
+ public void testHeaderReadFromOldController() {
+ mHeadsUpAppearanceController.setExpandedHeight(1.0f, 1.0f);
+
+ HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
+ mock(NotificationIconAreaController.class),
+ mHeadsUpManager,
+ mHeadsUpStatusBarView,
+ mStackScroller,
+ mPanelView,
+ new View(mContext));
+ newController.readFrom(mHeadsUpAppearanceController);
+
+ Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
+ newController.mExpandedHeight, 0.0f);
+ Assert.assertEquals(mHeadsUpAppearanceController.mExpandFraction,
+ newController.mExpandFraction, 0.0f);
+ Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
+ newController.mIsExpanded);
+ }
+
+ @Test
public void testDestroy() {
reset(mHeadsUpManager);
reset(mDarkIconDispatcher);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e95702c..89d562a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -44,7 +44,6 @@
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.util.function.TriConsumer;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.util.wakelock.WakeLock;
@@ -94,6 +93,7 @@
mScrimInFrontColor = scrimInFrontColor;
},
visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager);
+ mScrimController.setHasBackdrop(false);
}
@Test
@@ -140,12 +140,7 @@
@Test
public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
- ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
- @Override
- public boolean hasLockscreenWallpaper() {
- return true;
- }
- };
+ mScrimController.setHasBackdrop(true);
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 94ab9d2..6933328 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -60,7 +60,7 @@
@Mock
private NotificationPanelView mNotificationPanelView;
@Mock
- private FingerprintUnlockController mFingerprintUnlockController;
+ private BiometricUnlockController mBiometrucUnlockController;
@Mock
private DismissCallbackRegistry mDismissCallbackRegistry;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -72,7 +72,7 @@
mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(getContext(),
mViewMediatorCallback, mLockPatternUtils);
mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
- mNotificationPanelView, mFingerprintUnlockController, mDismissCallbackRegistry);
+ mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry);
mStatusBarKeyguardViewManager.show(null);
}
@@ -170,8 +170,8 @@
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
- when(mFingerprintUnlockController.getMode())
- .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+ when(mBiometrucUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
false /* tracking */);
verify(mBouncer, never()).setExpansion(anyFloat());
@@ -196,7 +196,7 @@
@Override
public void registerStatusBar(StatusBar statusBar, ViewGroup container,
NotificationPanelView notificationPanelView,
- FingerprintUnlockController fingerprintUnlockController,
+ BiometricUnlockController fingerprintUnlockController,
DismissCallbackRegistry dismissCallbackRegistry) {
super.registerStatusBar(statusBar, container, notificationPanelView,
fingerprintUnlockController, dismissCallbackRegistry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cf23bfc..f908dfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -132,7 +132,7 @@
@Mock private IStatusBarService mBarService;
@Mock private ScrimController mScrimController;
@Mock private ArrayList<Entry> mNotificationList;
- @Mock private FingerprintUnlockController mFingerprintUnlockController;
+ @Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
// Mock dependencies:
@@ -207,7 +207,7 @@
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
- mEntryManager, mScrimController, mFingerprintUnlockController,
+ mEntryManager, mScrimController, mBiometricUnlockController,
mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
mRemoteInputManager, mock(NotificationGroupManager.class),
mock(FalsingManager.class), mock(StatusBarWindowManager.class),
@@ -572,15 +572,15 @@
@Test
public void testFingerprintNotification_UpdatesScrims() {
- mStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyBiometricAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
@Test
public void testFingerprintUnlock_UpdatesScrims() {
// Simulate unlocking from AoD with fingerprint.
- when(mFingerprintUnlockController.getMode())
- .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBar.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@@ -717,7 +717,7 @@
VisualStabilityManager visualStabilityManager,
NotificationViewHierarchyManager viewHierarchyManager,
TestableNotificationEntryManager entryManager, ScrimController scrimController,
- FingerprintUnlockController fingerprintUnlockController,
+ BiometricUnlockController biometricUnlockController,
ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationGroupManager notificationGroupManager,
@@ -743,7 +743,7 @@
mViewHierarchyManager = viewHierarchyManager;
mEntryManager = entryManager;
mScrimController = scrimController;
- mFingerprintUnlockController = fingerprintUnlockController;
+ mBiometricUnlockController = biometricUnlockController;
mActivityLaunchAnimator = launchAnimator;
mKeyguardViewMediator = keyguardViewMediator;
mClearAllEnabled = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
index 8a74019..cfacf0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
@@ -40,30 +40,117 @@
private ExpandableNotificationRow mGroup;
private int mId;
private NotificationTestHelper mNotificationTestHelper;
+ private NotificationChildrenContainer mChildrenContainer;
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext);
mGroup = mNotificationTestHelper.createGroup();
+ mChildrenContainer = mGroup.getChildrenContainer();
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_ambient() {
+ mGroup.setShowAmbient(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_lowPriority() {
+ mChildrenContainer.setIsLowPriority(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_headsUp() {
+ mGroup.setHeadsUp(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_lowPriority_expandedChildren() {
+ mChildrenContainer.setIsLowPriority(true);
+ mChildrenContainer.setChildrenExpanded(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_lowPriority_userLocked() {
+ mChildrenContainer.setIsLowPriority(true);
+ mChildrenContainer.setUserLocked(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_likeCollapsed() {
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(true),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+ }
+
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_expandedChildren() {
+ mChildrenContainer.setChildrenExpanded(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_userLocked() {
+ mGroup.setUserLocked(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ }
+
+ @Test
+ public void testShowingAsLowPriority_lowPriority() {
+ mChildrenContainer.setIsLowPriority(true);
+ Assert.assertTrue(mChildrenContainer.showingAsLowPriority());
+ }
+
+ @Test
+ public void testShowingAsLowPriority_notLowPriority() {
+ Assert.assertFalse(mChildrenContainer.showingAsLowPriority());
+ }
+
+ @Test
+ public void testShowingAsLowPriority_lowPriority_expanded() {
+ mChildrenContainer.setIsLowPriority(true);
+ mGroup.setExpandable(true);
+ mGroup.setUserExpanded(true, false);
+ Assert.assertFalse(mChildrenContainer.showingAsLowPriority());
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_userLocked_expandedChildren_lowPriority() {
+ mGroup.setUserLocked(true);
+ mGroup.setExpandable(true);
+ mGroup.setUserExpanded(true);
+ mChildrenContainer.setIsLowPriority(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
}
@Test
public void testLowPriorityHeaderCleared() {
mGroup.setIsLowPriority(true);
- NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
- NotificationHeaderView lowPriorityHeaderView = childrenContainer.getLowPriorityHeaderView();
+ NotificationHeaderView lowPriorityHeaderView = mChildrenContainer.getLowPriorityHeaderView();
Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
- Assert.assertTrue(lowPriorityHeaderView.getParent() == childrenContainer);
+ Assert.assertTrue(lowPriorityHeaderView.getParent() == mChildrenContainer);
mGroup.setIsLowPriority(false);
Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
- Assert.assertTrue(childrenContainer.getLowPriorityHeaderView() == null);
+ Assert.assertTrue(mChildrenContainer.getLowPriorityHeaderView() == null);
}
@Test
public void testRecreateNotificationHeader_hasHeader() {
- NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
- childrenContainer.recreateNotificationHeader(null);
+ mChildrenContainer.recreateNotificationHeader(null);
Assert.assertNotNull("Children container must have a header after recreation",
- childrenContainer.getCurrentHeaderView());
+ mChildrenContainer.getCurrentHeaderView());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 50b4f3f..0d398be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -103,7 +103,7 @@
NotificationManager.IMPORTANCE_MIN);
NotificationChannel newChannel =
NotificationChannels.createScreenshotChannel("newName", legacyChannel);
- assertEquals(Uri.EMPTY, newChannel.getSound());
+ assertEquals(null, newChannel.getSound());
assertEquals("newName", newChannel.getName());
// MIN importance not user locked, so HIGH wins out.
assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
@@ -113,7 +113,7 @@
public void testInheritFromLegacy_noLegacyExists() {
NotificationChannel newChannel =
NotificationChannels.createScreenshotChannel("newName", null);
- assertEquals(Uri.EMPTY, newChannel.getSound());
+ assertEquals(null, newChannel.getSound());
assertEquals("newName", newChannel.getName());
assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
}
diff --git a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
deleted file mode 100644
index a0b4b61..0000000
--- a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
deleted file mode 100644
index df5dfe8..0000000
--- a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 18d5a3a..0000000
--- a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 4d475dc..0000000
--- a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 9d458b4..0000000
--- a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml b/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml
new file mode 100644
index 0000000..24f622e
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+</vector>
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d2990de..7905def 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6118,9 +6118,23 @@
// ---- End P Constants, all P constants go above this line ----
- // First Q constant in master goes here:
- // Please delete these lines and use 1500 for your first enum.
+ // Time since this notification last interrupted (visibly or audible) the user
+ NOTIFICATION_SINCE_INTERRUPTION_MILLIS = 1500;
+ // OPEN: Notification interrupted the user, either audibly or visually.
+ // Tagged data: NOTIFICATION_SINCE_INTERRUPTION_MILLIS
+ // CATEGORY: NOTIFICATION
+ NOTIFICATION_INTERRUPTION = 1501;
+
+ // OPEN: Settings
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_HOMEPAGE = 1502;
+
+ // OPEN: Settings > Create shortcut(widget)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_CREATE_SHORTCUT = 1503;
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index a9a14ca..fba639c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -212,6 +212,10 @@
// Package: android
NOTE_AUTO_SAVER_SUGGESTION = 49;
+ // Notify the user that their softap config preference has changed.
+ // Package: android
+ NOTE_SOFTAP_CONFIG_CHANGED = 50;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 72f11e0..33fc5e5 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -457,6 +457,19 @@
// Identifier for experimental scoring parameter settings.
optional string score_experiment_id = 117;
+ // Data on wifi radio usage
+ optional WifiRadioUsage wifi_radio_usage = 118;
+
+ // Stores settings values used for metrics testing.
+ optional ExperimentValues experiment_values = 119;
+
+ // List of WifiIsUnusableEvents which get logged when we notice that WiFi is unusable.
+ // Collected only when WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED Settings is enabled.
+ repeated WifiIsUnusableEvent wifi_is_unusable_event_list = 120;
+
+ // Counts the occurrences of each link speed (Mbps) level
+ // with rssi (dBm) and rssi^2 sums (dBm^2)
+ repeated LinkSpeedCount link_speed_counts = 121;
}
// Information that gets logged for every WiFi connection.
@@ -642,6 +655,22 @@
optional int32 count = 2;
}
+// Number of occurrences of a specific link speed (Mbps)
+// and sum of rssi (dBm) and rssi^2 (dBm^2)
+message LinkSpeedCount {
+ // Link speed (Mbps)
+ optional int32 link_speed_mbps = 1;
+
+ // Number of RSSI polls with link_speed
+ optional int32 count = 2;
+
+ // Sum of absolute values of rssi values (dBm)
+ optional int32 rssi_sum_dbm = 3;
+
+ // Sum of squares of rssi values (dBm^2)
+ optional int64 rssi_sum_of_squares_dbm_sq = 4;
+}
+
// Number of occurrences of Soft AP session durations
message SoftApDurationBucket {
// Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec)
@@ -874,7 +903,7 @@
STATE_INVALID = 12;
}
- // Bit mask of all supplicant state changes that occured since the last event
+ // Bit mask of all supplicant state changes that occurred since the last event
optional uint32 supplicant_state_changes_bitmask = 9 [default = 0];
// The number of milliseconds that have elapsed since the device booted
@@ -882,7 +911,7 @@
optional FrameworkDisconnectReason framework_disconnect_reason = 11 [default = DISCONNECT_UNKNOWN];
- // Flag which indicates if an association rejection event occured due to a timeout
+ // Flag which indicates if an association rejection event occurred due to a timeout
optional bool association_timed_out = 12 [default = false];
// Authentication failure reason, as reported by WifiManager (calculated from state & deauth code)
@@ -1503,3 +1532,83 @@
optional int32 count = 2;
}
}
+
+// Usage data for the wifi radio while device is running on battery.
+message WifiRadioUsage {
+ // Duration of log (ms)
+ optional int64 logging_duration_ms = 1;
+
+ // Total time for which the radio is awake due to scan.
+ optional int64 scan_time_ms = 2;
+}
+
+message ExperimentValues {
+ // Indicates if we are logging WifiIsUnusableEvent in metrics
+ optional bool wifi_is_unusable_logging_enabled = 1;
+
+ // Minimum number of txBad to trigger a data stall
+ optional int32 wifi_data_stall_min_tx_bad = 2;
+
+ // Minimum number of txSuccess to trigger a data stall
+ // when rxSuccess is 0
+ optional int32 wifi_data_stall_min_tx_success_without_rx = 3;
+
+ // Indicates if we are logging LinkSpeedCount in metrics
+ optional bool link_speed_counts_logging_enabled = 4;
+}
+
+message WifiIsUnusableEvent {
+ enum TriggerType {
+ // Default/Invalid event
+ TYPE_UNKNOWN = 0;
+
+ // There is a data stall from tx failures
+ TYPE_DATA_STALL_BAD_TX = 1;
+
+ // There is a data stall from rx failures
+ TYPE_DATA_STALL_TX_WITHOUT_RX = 2;
+
+ // There is a data stall from both tx and rx failures
+ TYPE_DATA_STALL_BOTH = 3;
+
+ // Firmware generated an alert
+ TYPE_FIRMWARE_ALERT = 4;
+ }
+
+ // What event triggered WifiIsUnusableEvent.
+ optional TriggerType type = 1;
+
+ // The timestamp at which this event occurred.
+ // Measured in milliseconds that have elapsed since the device booted.
+ optional int64 start_time_millis = 2;
+
+ // NetworkAgent score of connected wifi.
+ // Defaults to -1 if the score was never set.
+ optional int32 last_score = 3 [default = -1];
+
+ // Delta of successfully transmitted (ACKed) unicast data packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 tx_success_delta = 4;
+
+ // Delta of transmitted unicast data retry packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 tx_retries_delta = 5;
+
+ // Delta of lost (not ACKed) transmitted unicast data packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 tx_bad_delta = 6;
+
+ // Delta of received unicast data packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 rx_success_delta = 7;
+
+ // Time in millisecond between the last two WifiLinkLayerStats.
+ optional int64 packet_update_time_delta = 8;
+
+ // The timestamp at which the last WifiLinkLayerStats was updated.
+ // Measured in milliseconds that have elapsed since the device booted.
+ optional int64 last_link_layer_stats_update_time = 9;
+
+ // Firmware alert code. Only valid when the event was triggered by a firmware alert, otherwise -1.
+ optional int32 firmware_alert_code = 10 [default = -1];
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b2c1bed..376d16b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -20,7 +20,6 @@
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -30,7 +29,6 @@
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -111,10 +109,9 @@
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import libcore.util.EmptyArray;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
@@ -133,6 +130,8 @@
import java.util.function.Consumer;
import java.util.function.IntSupplier;
+import libcore.util.EmptyArray;
+
/**
* This class is instantiated by the system as a system level service and can be
* accessed only by the system. The task of this service is to be a centralized
@@ -2624,7 +2623,7 @@
PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
if (activityToken != null) {
- LocalServices.getService(ActivityManagerInternal.class)
+ LocalServices.getService(ActivityTaskManagerInternal.class)
.setFocusedActivity(activityToken);
}
connection.mConnection.performAccessibilityAction(accessibilityNodeId, action,
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6c359fe..4206d9a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -82,6 +82,7 @@
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d9dcf40..18255c5 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,8 +16,6 @@
package com.android.server.autofill;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
@@ -33,6 +31,8 @@
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -993,6 +993,7 @@
final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
final FillResponse authenticatedResponse = mResponses.get(requestId);
if (authenticatedResponse == null || data == null) {
+ Slog.w(TAG, "no authenticated response");
removeSelf();
return;
}
@@ -1003,6 +1004,7 @@
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx);
if (dataset == null) {
+ Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
removeSelf();
return;
}
@@ -1012,7 +1014,7 @@
final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
if (sDebug) {
Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
- + ", clientState=" + newClientState);
+ + ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
}
if (result instanceof FillResponse) {
logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
@@ -1029,6 +1031,8 @@
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
autoFill(requestId, datasetIdx, dataset, false);
} else {
+ Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
+ + authenticationId);
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
}
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
new file mode 100644
index 0000000..dc7044e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
@@ -0,0 +1,33 @@
+package com.android.server.backup.restore;
+
+import android.app.IBackupAgent;
+import android.os.RemoteException;
+
+import com.android.server.backup.BackupManagerService;
+
+/**
+ * Runner that can be placed on a separate thread to do in-process invocation of the "restore
+ * finished" API asynchronously. Used by adb restore.
+ */
+public class AdbRestoreFinishedRunnable implements Runnable {
+
+ private final IBackupAgent mAgent;
+ private final int mToken;
+ private final BackupManagerService mBackupManagerService;
+
+ AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
+ BackupManagerService backupManagerService) {
+ mAgent = agent;
+ mToken = token;
+ mBackupManagerService = backupManagerService;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
+ } catch (RemoteException e) {
+ // never happens; this is used only for local binder calls
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 6bc7530..1084f52 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -122,15 +122,17 @@
// Widget blob to be restored out-of-band
private byte[] mWidgetData = null;
+ private long mAppVersion;
final int mEphemeralOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ final boolean mIsAdbRestore;
public FullRestoreEngine(BackupManagerService backupManagerService,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
- boolean allowObbs, int ephemeralOpToken) {
+ boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
mBackupManagerService = backupManagerService;
mEphemeralOpToken = ephemeralOpToken;
mMonitorTask = monitorTask;
@@ -144,6 +146,7 @@
mAgentTimeoutParameters = Preconditions.checkNotNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
+ mIsAdbRestore = isAdbRestore;
}
public IBackupAgent getAgent() {
@@ -209,7 +212,7 @@
}
// Now we're really done
tearDownPipes();
- tearDownAgent(mTargetApp);
+ tearDownAgent(mTargetApp, mIsAdbRestore);
mTargetApp = null;
mAgentPackage = null;
}
@@ -218,6 +221,9 @@
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
info);
+ // readAppManifestAndReturnSignatures() will have extracted the version from
+ // the manifest, so we save it to use in adb key-value restore later.
+ mAppVersion = info.version;
PackageManagerInternal pmi = LocalServices.getService(
PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
@@ -362,7 +368,9 @@
// All set; now set up the IPC and launch the agent
setUpPipes();
mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
- ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
+ FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
+ ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+ : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
mAgentPackage = pkg;
} catch (IOException e) {
// fall through to error handling
@@ -419,6 +427,8 @@
Slog.d(TAG, "Restoring key-value file for " + pkg
+ " : " + info.path);
}
+ // Set the version saved from manifest entry.
+ info.version = mAppVersion;
KeyValueAdbRestoreEngine restoreEngine =
new KeyValueAdbRestoreEngine(
mBackupManagerService,
@@ -506,7 +516,7 @@
mBackupManagerService.getBackupHandler().removeMessages(
MSG_RESTORE_OPERATION_TIMEOUT);
tearDownPipes();
- tearDownAgent(mTargetApp);
+ tearDownAgent(mTargetApp, false);
mAgent = null;
mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
@@ -559,7 +569,7 @@
tearDownPipes();
setRunning(false);
if (mustKillAgent) {
- tearDownAgent(mTargetApp);
+ tearDownAgent(mTargetApp, mIsAdbRestore);
}
}
return (info != null);
@@ -588,9 +598,37 @@
}
}
- private void tearDownAgent(ApplicationInfo app) {
+ private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
if (mAgent != null) {
- mBackupManagerService.tearDownAgentAndKill(app);
+ try {
+ // In the adb restore case, we do restore-finished here
+ if (doRestoreFinished) {
+ final int token = mBackupManagerService.generateRandomIntegerToken();
+ long fullBackupAgentTimeoutMillis =
+ mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
+ final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
+ mBackupManagerService, token);
+ mBackupManagerService.prepareOperationTimeout(
+ token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
+ if (mTargetApp.processName.equals("system")) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "system agent - restoreFinished on thread");
+ }
+ Runnable runner = new AdbRestoreFinishedRunnable(mAgent, token,
+ mBackupManagerService);
+ new Thread(runner, "restore-sys-finished-runner").start();
+ } else {
+ mAgent.doRestoreFinished(token,
+ mBackupManagerService.getBackupManagerBinder());
+ }
+
+ latch.await();
+ }
+
+ mBackupManagerService.tearDownAgentAndKill(app);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
mAgent = null;
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
new file mode 100644
index 0000000..7075608
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngineThread.java
@@ -0,0 +1,63 @@
+package com.android.server.backup.restore;
+
+import android.os.ParcelFileDescriptor;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+class FullRestoreEngineThread implements Runnable {
+
+ FullRestoreEngine mEngine;
+ InputStream mEngineStream;
+ private final boolean mMustKillAgent;
+
+ FullRestoreEngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
+ mEngine = engine;
+ engine.setRunning(true);
+ // We *do* want this FileInputStream to own the underlying fd, so that
+ // when we are finished with it, it closes this end of the pipe in a way
+ // that signals its other end.
+ mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
+ // Tell it to be sure to leave the agent instance up after finishing
+ mMustKillAgent = false;
+ }
+
+ //for adb restore
+ FullRestoreEngineThread(FullRestoreEngine engine, InputStream inputStream) {
+ mEngine = engine;
+ engine.setRunning(true);
+ mEngineStream = inputStream;
+ // philippov: in adb agent is killed after restore.
+ mMustKillAgent = true;
+ }
+
+ public boolean isRunning() {
+ return mEngine.isRunning();
+ }
+
+ public int waitForResult() {
+ return mEngine.waitForResult();
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (mEngine.isRunning()) {
+ mEngine.restoreOneFile(mEngineStream, mMustKillAgent, mEngine.mBuffer,
+ mEngine.mOnlyPackage, mEngine.mAllowApks, mEngine.mEphemeralOpToken,
+ mEngine.mMonitor);
+ }
+ } finally {
+ // Because mEngineStream adopted its underlying FD, this also
+ // closes this end of the pipe.
+ IoUtils.closeQuietly(mEngineStream);
+ }
+ }
+
+ public void handleTimeout() {
+ IoUtils.closeQuietly(mEngineStream);
+ mEngine.handleTimeout();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 0c99b44..78b000d 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -18,8 +18,6 @@
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
@@ -30,15 +28,10 @@
import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-import android.app.ApplicationThreadConstants;
+
import android.app.IBackupAgent;
-import android.app.backup.FullBackup;
-import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IFullBackupRestoreObserver;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
@@ -50,18 +43,12 @@
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.FileMetadata;
-import com.android.server.backup.KeyValueAdbRestoreEngine;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
-import com.android.server.backup.utils.BytesReadListener;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
import com.android.server.backup.utils.PasswordUtils;
-import com.android.server.backup.utils.RestoreUtils;
-import com.android.server.backup.utils.TarBackupReader;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
@@ -104,31 +91,6 @@
private long mBytes;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- // Runner that can be placed on a separate thread to do in-process invocation
- // of the "restore finished" API asynchronously. Used by adb restore.
- private static class RestoreFinishedRunnable implements Runnable {
-
- private final IBackupAgent mAgent;
- private final int mToken;
- private final BackupManagerService mBackupManagerService;
-
- RestoreFinishedRunnable(IBackupAgent agent, int token,
- BackupManagerService backupManagerService) {
- mAgent = agent;
- mToken = token;
- mBackupManagerService = backupManagerService;
- }
-
- @Override
- public void run() {
- try {
- mAgent.doRestoreFinished(mToken, mBackupManagerService.getBackupManagerBinder());
- } catch (RemoteException e) {
- // never happens; this is used only for local binder calls
- }
- }
- }
-
// possible handling states for a given package in the restore dataset
private final HashMap<String, RestorePolicy> mPackagePolicies
= new HashMap<>();
@@ -199,23 +161,18 @@
return;
}
- byte[] buffer = new byte[32 * 1024];
- boolean didRestore;
- do {
- didRestore = restoreOneFile(tarInputStream, false /* mustKillAgent */, buffer,
- null /* onlyPackage */, true /* allowApks */,
- mBackupManagerService.generateRandomIntegerToken(), null /* monitor */);
- } while (didRestore);
+ FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
+ mObserver, null, null, true, true/*unused*/, 0 /*unused*/, true);
+ FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
+ tarInputStream);
+ mEngineThread.run();
if (MORE_DEBUG) {
- Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
+ Slog.v(TAG, "Done consuming input tarfile.");
}
} catch (IOException e) {
Slog.e(TAG, "Unable to read restore input");
} finally {
- tearDownPipes();
- tearDownAgent(mTargetApp, true);
-
try {
if (rawInStream != null) {
rawInStream.close();
@@ -433,432 +390,4 @@
return result;
}
-
- boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer,
- PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) {
- BytesReadListener bytesReadListener = new BytesReadListener() {
- @Override
- public void onBytesRead(long bytesRead) {
- mBytes += bytesRead;
- }
- };
- TarBackupReader tarBackupReader = new TarBackupReader(instream,
- bytesReadListener, monitor);
- FileMetadata info;
- try {
- info = tarBackupReader.readTarHeaders();
- if (info != null) {
- if (MORE_DEBUG) {
- info.dump();
- }
-
- final String pkg = info.packageName;
- if (!pkg.equals(mAgentPackage)) {
- // okay, change in package; set up our various
- // bookkeeping if we haven't seen it yet
- if (!mPackagePolicies.containsKey(pkg)) {
- mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
- }
-
- // Clean up the previous agent relationship if necessary,
- // and let the observer know we're considering a new app.
- if (mAgent != null) {
- if (DEBUG) {
- Slog.d(TAG, "Saw new package; finalizing old one");
- }
- // Now we're really done
- tearDownPipes();
- tearDownAgent(mTargetApp, true);
- mTargetApp = null;
- mAgentPackage = null;
- }
- }
-
- if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
- Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
- info);
- // readAppManifestAndReturnSignatures() will have extracted the version from
- // the manifest, so we save it to use in key-value restore later.
- mAppVersion = info.version;
- PackageManagerInternal pmi = LocalServices.getService(
- PackageManagerInternal.class);
- RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
- mBackupManagerService.getPackageManager(), allowApks,
- info, signatures, pmi);
- mManifestSignatures.put(info.packageName, signatures);
- mPackagePolicies.put(pkg, restorePolicy);
- mPackageInstallers.put(pkg, info.installerPackageName);
- // We've read only the manifest content itself at this point,
- // so consume the footer before looping around to the next
- // input file
- tarBackupReader.skipTarPadding(info.size);
- mObserver = FullBackupRestoreObserverUtils.sendOnRestorePackage(mObserver, pkg);
- } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
- // Metadata blobs!
- tarBackupReader.readMetadata(info);
-
- // The following only exist because we want to keep refactoring as safe as
- // possible, without changing too much.
- // TODO: Refactor, so that there are no funny things like this.
- // This is read during TarBackupReader.readMetadata().
- mWidgetData = tarBackupReader.getWidgetData();
- // This can be nulled during TarBackupReader.readMetadata().
- monitor = tarBackupReader.getMonitor();
-
- tarBackupReader.skipTarPadding(info.size);
- } else {
- // Non-manifest, so it's actual file data. Is this a package
- // we're ignoring?
- boolean okay = true;
- RestorePolicy policy = mPackagePolicies.get(pkg);
- switch (policy) {
- case IGNORE:
- okay = false;
- break;
-
- case ACCEPT_IF_APK:
- // If we're in accept-if-apk state, then the first file we
- // see MUST be the apk.
- if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
- if (DEBUG) {
- Slog.d(TAG, "APK file; installing");
- }
- // Try to install the app.
- String installerPackageName = mPackageInstallers.get(pkg);
- boolean isSuccessfullyInstalled = RestoreUtils.installApk(instream,
- mBackupManagerService.getContext(),
- mDeleteObserver, mManifestSignatures, mPackagePolicies,
- info, installerPackageName, bytesReadListener);
- // good to go; promote to ACCEPT
- mPackagePolicies.put(pkg, isSuccessfullyInstalled
- ? RestorePolicy.ACCEPT
- : RestorePolicy.IGNORE);
- // At this point we've consumed this file entry
- // ourselves, so just strip the tar footer and
- // go on to the next file in the input stream
- tarBackupReader.skipTarPadding(info.size);
- return true;
- } else {
- // File data before (or without) the apk. We can't
- // handle it coherently in this case so ignore it.
- mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
- okay = false;
- }
- break;
-
- case ACCEPT:
- if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
- if (DEBUG) {
- Slog.d(TAG, "apk present but ACCEPT");
- }
- // we can take the data without the apk, so we
- // *want* to do so. skip the apk by declaring this
- // one file not-okay without changing the restore
- // policy for the package.
- okay = false;
- }
- break;
-
- default:
- // Something has gone dreadfully wrong when determining
- // the restore policy from the manifest. Ignore the
- // rest of this package's data.
- Slog.e(TAG, "Invalid policy from manifest");
- okay = false;
- mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
- break;
- }
-
- // The path needs to be canonical
- if (!isCanonicalFilePath(info.path)) {
- okay = false;
- }
-
- // If the policy is satisfied, go ahead and set up to pipe the
- // data to the agent.
- if (DEBUG && okay && mAgent != null) {
- Slog.i(TAG, "Reusing existing agent instance");
- }
- if (okay && mAgent == null) {
- if (DEBUG) {
- Slog.d(TAG, "Need to launch agent for " + pkg);
- }
-
- try {
- mTargetApp =
- mBackupManagerService.getPackageManager().getApplicationInfo(
- pkg, 0);
-
- // If we haven't sent any data to this app yet, we probably
- // need to clear it first. Check that.
- if (!mClearedPackages.contains(pkg)) {
- // apps with their own backup agents are
- // responsible for coherently managing a full
- // restore.
- if (mTargetApp.backupAgentName == null) {
- if (DEBUG) {
- Slog.d(TAG,
- "Clearing app data preparatory to full restore");
- }
- mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
- } else {
- if (DEBUG) {
- Slog.d(TAG, "backup agent ("
- + mTargetApp.backupAgentName + ") => no clear");
- }
- }
- mClearedPackages.add(pkg);
- } else {
- if (DEBUG) {
- Slog.d(TAG, "We've initialized this app already; no clear "
- + "required");
- }
- }
-
- // All set; now set up the IPC and launch the agent
- setUpPipes();
- mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
- FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
- ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
- : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
- mAgentPackage = pkg;
- } catch (IOException e) {
- // fall through to error handling
- } catch (NameNotFoundException e) {
- // fall through to error handling
- }
-
- if (mAgent == null) {
- Slog.e(TAG, "Unable to create agent for " + pkg);
- okay = false;
- tearDownPipes();
- mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
- }
- }
-
- // Sanity check: make sure we never give data to the wrong app. This
- // should never happen but a little paranoia here won't go amiss.
- if (okay && !pkg.equals(mAgentPackage)) {
- Slog.e(TAG, "Restoring data for " + pkg
- + " but agent is for " + mAgentPackage);
- okay = false;
- }
-
- // At this point we have an agent ready to handle the full
- // restore data as well as a pipe for sending data to
- // that agent. Tell the agent to start reading from the
- // pipe.
- if (okay) {
- boolean agentSuccess = true;
- long toCopy = info.size;
- long restoreAgentTimeoutMillis =
- mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
- try {
- mBackupManagerService.prepareOperationTimeout(
- token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
-
- if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
- if (DEBUG) {
- Slog.d(TAG, "Restoring OBB file for " + pkg
- + " : " + info.path);
- }
- mObbConnection.restoreObbFile(pkg, mPipes[0],
- info.size, info.type, info.path, info.mode,
- info.mtime, token,
- mBackupManagerService.getBackupManagerBinder());
- } else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) {
- if (DEBUG) {
- Slog.d(TAG, "Restoring key-value file for " + pkg
- + " : " + info.path);
- }
- // Set the version saved from manifest entry.
- info.version = mAppVersion;
- KeyValueAdbRestoreEngine restoreEngine =
- new KeyValueAdbRestoreEngine(
- mBackupManagerService,
- mBackupManagerService.getDataDir(), info, mPipes[0],
- mAgent, token);
- new Thread(restoreEngine, "restore-key-value-runner").start();
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Invoking agent to restore file " + info.path);
- }
- // fire up the app's agent listening on the socket. If
- // the agent is running in the system process we can't
- // just invoke it asynchronously, so we provide a thread
- // for it here.
- if (mTargetApp.processName.equals("system")) {
- Slog.d(TAG, "system process agent - spinning a thread");
- RestoreFileRunnable runner = new RestoreFileRunnable(
- mBackupManagerService, mAgent, info, mPipes[0], token);
- new Thread(runner, "restore-sys-runner").start();
- } else {
- mAgent.doRestoreFile(mPipes[0], info.size, info.type,
- info.domain, info.path, info.mode, info.mtime,
- token, mBackupManagerService.getBackupManagerBinder());
- }
- }
- } catch (IOException e) {
- // couldn't dup the socket for a process-local restore
- Slog.d(TAG, "Couldn't establish restore");
- agentSuccess = false;
- okay = false;
- } catch (RemoteException e) {
- // whoops, remote entity went away. We'll eat the content
- // ourselves, then, and not copy it over.
- Slog.e(TAG, "Agent crashed during full restore");
- agentSuccess = false;
- okay = false;
- }
-
- // Copy over the data if the agent is still good
- if (okay) {
- boolean pipeOkay = true;
- FileOutputStream pipe = new FileOutputStream(
- mPipes[1].getFileDescriptor());
- while (toCopy > 0) {
- int toRead = (toCopy > buffer.length)
- ? buffer.length : (int) toCopy;
- int nRead = instream.read(buffer, 0, toRead);
- if (nRead >= 0) {
- mBytes += nRead;
- }
- if (nRead <= 0) {
- break;
- }
- toCopy -= nRead;
-
- // send it to the output pipe as long as things
- // are still good
- if (pipeOkay) {
- try {
- pipe.write(buffer, 0, nRead);
- } catch (IOException e) {
- Slog.e(TAG, "Failed to write to restore pipe", e);
- pipeOkay = false;
- }
- }
- }
-
- // done sending that file! Now we just need to consume
- // the delta from info.size to the end of block.
- tarBackupReader.skipTarPadding(info.size);
-
- // and now that we've sent it all, wait for the remote
- // side to acknowledge receipt
- agentSuccess = mBackupManagerService.waitUntilOperationComplete(token);
- }
-
- // okay, if the remote end failed at any point, deal with
- // it by ignoring the rest of the restore on it
- if (!agentSuccess) {
- if (DEBUG) {
- Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
- }
- mBackupManagerService.getBackupHandler().removeMessages(
- MSG_RESTORE_OPERATION_TIMEOUT);
- tearDownPipes();
- tearDownAgent(mTargetApp, false);
- mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
- }
- }
-
- // Problems setting up the agent communication, or an already-
- // ignored package: skip to the next tar stream entry by
- // reading and discarding this file.
- if (!okay) {
- if (DEBUG) {
- Slog.d(TAG, "[discarding file content]");
- }
- long bytesToConsume = (info.size + 511) & ~511;
- while (bytesToConsume > 0) {
- int toRead = (bytesToConsume > buffer.length)
- ? buffer.length : (int) bytesToConsume;
- long nRead = instream.read(buffer, 0, toRead);
- if (nRead >= 0) {
- mBytes += nRead;
- }
- if (nRead <= 0) {
- break;
- }
- bytesToConsume -= nRead;
- }
- }
- }
- }
- } catch (IOException e) {
- if (DEBUG) {
- Slog.w(TAG, "io exception on restore socket read", e);
- }
- // treat as EOF
- info = null;
- }
-
- return (info != null);
- }
-
- private static boolean isCanonicalFilePath(String path) {
- if (path.contains("..") || path.contains("//")) {
- if (MORE_DEBUG) {
- Slog.w(TAG, "Dropping invalid path " + path);
- }
- return false;
- }
-
- return true;
- }
-
- private void setUpPipes() throws IOException {
- mPipes = ParcelFileDescriptor.createPipe();
- }
-
- private void tearDownPipes() {
- if (mPipes != null) {
- try {
- mPipes[0].close();
- mPipes[0] = null;
- mPipes[1].close();
- mPipes[1] = null;
- } catch (IOException e) {
- Slog.w(TAG, "Couldn't close agent pipes", e);
- }
- mPipes = null;
- }
- }
-
- private void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
- if (mAgent != null) {
- try {
- // In the adb restore case, we do restore-finished here
- if (doRestoreFinished) {
- final int token = mBackupManagerService.generateRandomIntegerToken();
- long fullBackupAgentTimeoutMillis =
- mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
- final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
- mBackupManagerService, token);
- mBackupManagerService.prepareOperationTimeout(
- token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
- if (mTargetApp.processName.equals("system")) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "system agent - restoreFinished on thread");
- }
- Runnable runner = new RestoreFinishedRunnable(mAgent, token,
- mBackupManagerService);
- new Thread(runner, "restore-sys-finished-runner").start();
- } else {
- mAgent.doRestoreFinished(token,
- mBackupManagerService.getBackupManagerBinder());
- }
-
- latch.await();
- }
-
- mBackupManagerService.tearDownAgentAndKill(app);
- } catch (RemoteException e) {
- Slog.d(TAG, "Lost app trying to shut down");
- }
- mAgent = null;
- }
- }
-
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 12d72d8..580f70a 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -841,7 +841,7 @@
final String TAG = "StreamFeederThread";
FullRestoreEngine mEngine;
- EngineThread mEngineThread;
+ FullRestoreEngineThread mEngineThread;
// pipe through which we read data from the transport. [0] read, [1] write
ParcelFileDescriptor[] mTransportPipes;
@@ -867,8 +867,8 @@
mCurrentPackage.packageName);
mEngine = new FullRestoreEngine(backupManagerService, this, null,
- mMonitor, mCurrentPackage, false, false, mEphemeralOpToken);
- mEngineThread = new EngineThread(mEngine, mEnginePipes[0]);
+ mMonitor, mCurrentPackage, false, false, mEphemeralOpToken, false);
+ mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
ParcelFileDescriptor tReadEnd = mTransportPipes[0];
@@ -1031,50 +1031,6 @@
}
}
- class EngineThread implements Runnable {
-
- FullRestoreEngine mEngine;
- FileInputStream mEngineStream;
-
- EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
- mEngine = engine;
- engine.setRunning(true);
- // We *do* want this FileInputStream to own the underlying fd, so that
- // when we are finished with it, it closes this end of the pipe in a way
- // that signals its other end.
- mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
- }
-
- public boolean isRunning() {
- return mEngine.isRunning();
- }
-
- public int waitForResult() {
- return mEngine.waitForResult();
- }
-
- @Override
- public void run() {
- try {
- while (mEngine.isRunning()) {
- // Tell it to be sure to leave the agent instance up after finishing
- mEngine.restoreOneFile(mEngineStream, false, mEngine.mBuffer,
- mEngine.mOnlyPackage, mEngine.mAllowApks, mEngine.mEphemeralOpToken,
- mEngine.mMonitor);
- }
- } finally {
- // Because mEngineStream adopted its underlying FD, this also
- // closes this end of the pipe.
- IoUtils.closeQuietly(mEngineStream);
- }
- }
-
- public void handleTimeout() {
- IoUtils.closeQuietly(mEngineStream);
- mEngine.handleTimeout();
- }
- }
-
// state FINAL : tear everything down and we're done.
private void finalizeRestore() {
if (MORE_DEBUG) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8da6d1e..ee7a5e2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -37,6 +37,7 @@
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
"android.hardware.weaver-V1.0-java",
+ "android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 6ca81c2..3261209 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -172,7 +172,8 @@
long mNativeData;
private long mNextWakeup;
private long mNextNonWakeup;
- private long mLastWakeupSet;
+ private long mNextWakeUpSetAt;
+ private long mNextNonWakeUpSetAt;
private long mLastWakeup;
private long mLastTrigger;
private long mLastTickSet;
@@ -1939,7 +1940,7 @@
TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
pw.println();
pw.print(" Next non-wakeup delivery time: ");
- TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
+ TimeUtils.formatDuration(mNextNonWakeupDeliveryTime, nowELAPSED, pw);
pw.println();
long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
@@ -1948,10 +1949,12 @@
TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
pw.print(" = "); pw.print(mNextNonWakeup);
pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
+ pw.print(" set at "); TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
+ pw.println();
pw.print(" Next wakeup alarm: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
pw.print(" = "); pw.print(mNextWakeup);
pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
- pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
+ pw.print(" set at "); TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
pw.println();
pw.print(" Next kernel non-wakeup alarm: ");
@@ -2290,7 +2293,7 @@
proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_MS,
nowElapsed - mLastWakeup);
proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
- nowElapsed - mLastWakeupSet);
+ nowElapsed - mNextWakeUpSetAt);
proto.write(AlarmManagerServiceDumpProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
final TreeSet<Integer> users = new TreeSet<>();
@@ -2675,16 +2678,49 @@
DateFormat.format(pattern, info.getTriggerTime()).toString();
}
+ /**
+ * If the last time AlarmThread woke up precedes any due wakeup or non-wakeup alarm that we set
+ * by more than half a minute, log a wtf.
+ */
+ private void validateLastAlarmExpiredLocked(long nowElapsed) {
+ final StringBuilder errorMsg = new StringBuilder();
+ boolean stuck = false;
+ if (mNextNonWakeup < nowElapsed && mLastWakeup < (mNextNonWakeup - 30_000)) {
+ stuck = true;
+ errorMsg.append("[mNextNonWakeup=");
+ TimeUtils.formatDuration(mNextNonWakeup - nowElapsed, errorMsg);
+ errorMsg.append(", mLastWakeup=");
+ TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
+ errorMsg.append(", timerfd_gettime=" + getNextAlarm(mNativeData, ELAPSED_REALTIME));
+ errorMsg.append("];");
+ }
+ if (mNextWakeup < nowElapsed && mLastWakeup < (mNextWakeup - 30_000)) {
+ stuck = true;
+ errorMsg.append("[mNextWakeup=");
+ TimeUtils.formatDuration(mNextWakeup - nowElapsed, errorMsg);
+ errorMsg.append(", mLastWakeup=");
+ TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
+ errorMsg.append(", timerfd_gettime="
+ + getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP));
+ errorMsg.append("];");
+ }
+ if (stuck) {
+ Slog.wtf(TAG, "Alarm delivery stuck: " + errorMsg.toString());
+ }
+ }
+
void rescheduleKernelAlarmsLocked() {
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ validateLastAlarmExpiredLocked(nowElapsed);
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
if (firstWakeup != null) {
mNextWakeup = firstWakeup.start;
- mLastWakeupSet = SystemClock.elapsedRealtime();
+ mNextWakeUpSetAt = nowElapsed;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
if (firstBatch != firstWakeup) {
@@ -2698,6 +2734,7 @@
}
if (nextNonWakeup != 0) {
mNextNonWakeup = nextNonWakeup;
+ mNextNonWakeUpSetAt = nowElapsed;
setLocked(ELAPSED_REALTIME, nextNonWakeup);
}
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 786d757..1167e1d 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -198,7 +198,7 @@
@VisibleForTesting
final SparseArray<UidState> mUidStates = new SparseArray<>();
- long mLastUptime;
+ long mLastRealtime;
/*
* These are app op restrictions imposed per user from various parties.
@@ -770,7 +770,7 @@
} else {
settleTime = mConstants.BG_STATE_SETTLE_TIME;
}
- uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + settleTime;
+ uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
}
if (uidState.startNesting != 0) {
// There is some actively running operation... need to find it
@@ -1881,11 +1881,11 @@
mUidStates.put(uid, uidState);
} else {
if (uidState.pendingStateCommitTime != 0) {
- if (uidState.pendingStateCommitTime < mLastUptime) {
+ if (uidState.pendingStateCommitTime < mLastRealtime) {
commitUidPendingStateLocked(uidState);
} else {
- mLastUptime = SystemClock.uptimeMillis();
- if (uidState.pendingStateCommitTime < mLastUptime) {
+ mLastRealtime = SystemClock.elapsedRealtime();
+ if (uidState.pendingStateCommitTime < mLastRealtime) {
commitUidPendingStateLocked(uidState);
}
}
@@ -3284,7 +3284,7 @@
}
if (uidState.pendingStateCommitTime != 0) {
pw.print(" pendingStateCommitTime=");
- TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowUptime, pw);
+ TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowElapsed, pw);
pw.println();
}
if (uidState.startNesting != 0) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f14b877..3c94a34 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -898,6 +898,10 @@
public boolean isTetheringSupported() {
return ConnectivityService.this.isTetheringSupported();
}
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ return mDefaultRequest;
+ }
};
return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
@@ -915,7 +919,7 @@
private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) {
- NetworkCapabilities netCap = new NetworkCapabilities();
+ final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
if (transportType > -1) {
@@ -1020,7 +1024,8 @@
}
}
- private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
+ @VisibleForTesting
+ protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
if (network == null) {
return null;
}
@@ -3378,35 +3383,7 @@
&& Uri.EMPTY.equals(proxy.getPacFileUrl())) {
proxy = null;
}
- synchronized (mProxyTracker.mProxyLock) {
- if (mProxyTracker.mDefaultProxy != null && mProxyTracker.mDefaultProxy.equals(proxy)) {
- return;
- }
- if (mProxyTracker.mDefaultProxy == proxy) return; // catches repeated nulls
- if (proxy != null && !proxy.isValid()) {
- if (DBG) log("Invalid proxy properties, ignoring: " + proxy.toString());
- return;
- }
-
- // This call could be coming from the PacManager, containing the port of the local
- // proxy. If this new proxy matches the global proxy then copy this proxy to the
- // global (to get the correct local port), and send a broadcast.
- // TODO: Switch PacManager to have its own message to send back rather than
- // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
- if ((mProxyTracker.mGlobalProxy != null) && (proxy != null)
- && (!Uri.EMPTY.equals(proxy.getPacFileUrl()))
- && proxy.getPacFileUrl().equals(mProxyTracker.mGlobalProxy.getPacFileUrl())) {
- mProxyTracker.mGlobalProxy = proxy;
- mProxyTracker.sendProxyBroadcast(mProxyTracker.mGlobalProxy);
- return;
- }
- mProxyTracker.mDefaultProxy = proxy;
-
- if (mProxyTracker.mGlobalProxy != null) return;
- if (!mProxyTracker.mDefaultProxyDisabled) {
- mProxyTracker.sendProxyBroadcast(proxy);
- }
- }
+ mProxyTracker.setDefaultProxy(proxy);
}
// If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b8f395b..a34c2b9 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -30,15 +30,15 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
-import android.location.LocationRequest;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
+import android.location.LocationRequest;
import android.net.ConnectivityManager;
import android.net.INetworkPolicyManager;
import android.net.NetworkInfo;
@@ -82,10 +82,10 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -118,6 +118,7 @@
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
private ActivityManagerInternal mLocalActivityManager;
+ private ActivityTaskManagerInternal mLocalActivityTaskManager;
private PowerManagerInternal mLocalPowerManager;
private PowerManager mPowerManager;
private ConnectivityService mConnectivityService;
@@ -1359,8 +1360,8 @@
}
}
- private ActivityManagerInternal.ScreenObserver mScreenObserver =
- new ActivityManagerInternal.ScreenObserver() {
+ private ActivityTaskManagerInternal.ScreenObserver mScreenObserver =
+ new ActivityTaskManagerInternal.ScreenObserver() {
@Override
public void onAwakeStateChanged(boolean isAwake) { }
@@ -1461,6 +1462,7 @@
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mBatteryStats = BatteryStatsService.getService();
mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
+ mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
mPowerManager = getContext().getSystemService(PowerManager.class);
mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -1539,7 +1541,7 @@
mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
- mLocalActivityManager.registerScreenObserver(mScreenObserver);
+ mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);
passWhiteListsToForceAppStandbyTrackerLocked();
updateInteractivityLocked();
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index eedafae..4b8ece9 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -324,6 +324,15 @@
// Used to bring IME service up to visible adjustment while it is being shown.
final ServiceConnection mVisibleConnection = new ServiceConnection() {
+ @Override public void onBindingDied(ComponentName name) {
+ synchronized (mMethodMap) {
+ if (mVisibleBound) {
+ mContext.unbindService(mVisibleConnection);
+ mVisibleBound = false;
+ }
+ }
+ }
+
@Override public void onServiceConnected(ComponentName name, IBinder service) {
}
@@ -459,6 +468,13 @@
IBinder mCurFocusedWindow;
/**
+ * The last window token that we confirmed that IME started talking to. This is always updated
+ * upon reports from the input method. If the window state is already changed before the report
+ * is handled, this field just keeps the last value.
+ */
+ IBinder mLastImeTargetWindow;
+
+ /**
* {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
*
* @see #mCurFocusedWindow
@@ -679,7 +695,7 @@
}
@GuardedBy("mMethodMap")
- private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
+ private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
/**
* A ring buffer to store the history of {@link StartInputInfo}.
@@ -1800,7 +1816,7 @@
final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
!initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
mCurSeq);
- mStartInputMap.put(startInputToken, info);
+ mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
mStartInputHistory.addEntry(info);
final SessionState session = mCurClient.curSession;
@@ -1818,50 +1834,6 @@
@GuardedBy("mMethodMap")
@NonNull
- InputBindResult startInputLocked(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- @Nullable EditorInfo attribute, int controlFlags) {
- // If no method is currently selected, do nothing.
- if (mCurMethodId == null) {
- return InputBindResult.NO_IME;
- }
-
- ClientState cs = mClients.get(client.asBinder());
- if (cs == null) {
- throw new IllegalArgumentException("unknown client "
- + client.asBinder());
- }
-
- if (attribute == null) {
- Slog.w(TAG, "Ignoring startInput with null EditorInfo."
- + " uid=" + cs.uid + " pid=" + cs.pid);
- return InputBindResult.NULL_EDITOR_INFO;
- }
-
- try {
- if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
- // Check with the window manager to make sure this client actually
- // has a window with focus. If not, reject. This is thread safe
- // because if the focus changes some time before or after, the
- // next client receiving focus that has any interest in input will
- // be calling through here after that change happens.
- if (DEBUG) {
- Slog.w(TAG, "Starting input on non-focused client " + cs.client
- + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
- }
- return InputBindResult.NOT_IME_TARGET_WINDOW;
- }
- } catch (RemoteException e) {
- }
-
- return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
- controlFlags, startInputReason);
- }
-
- @GuardedBy("mMethodMap")
- @NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@NonNull EditorInfo attribute, int controlFlags,
@@ -1988,36 +1960,6 @@
return InputBindResult.IME_NOT_CONNECTED;
}
- @NonNull
- private InputBindResult startInput(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- @Nullable EditorInfo attribute, int controlFlags) {
- if (!calledFromValidUser()) {
- return InputBindResult.INVALID_USER;
- }
- synchronized (mMethodMap) {
- if (DEBUG) {
- Slog.v(TAG, "startInput: reason="
- + InputMethodClient.getStartInputReason(startInputReason)
- + " client = " + client.asBinder()
- + " inputContext=" + inputContext
- + " missingMethods="
- + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
- + " attribute=" + attribute
- + " controlFlags=#" + Integer.toHexString(controlFlags));
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- return startInputLocked(startInputReason, client, inputContext, missingMethods,
- attribute, controlFlags);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
@Override
public void finishInput(IInputMethodClient client) {
}
@@ -2284,15 +2226,12 @@
@BinderThread
@SuppressWarnings("deprecation")
@Override
- public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
- int backDisposition) {
+ public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
if (!calledWithValidToken(token)) {
return;
}
- final StartInputInfo info;
synchronized (mMethodMap) {
- info = mStartInputMap.get(startInputToken);
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(token, vis, backDisposition);
@@ -2312,8 +2251,7 @@
break;
}
mWindowManagerInternal.updateInputMethodWindowStatus(token,
- (vis & InputMethodService.IME_VISIBLE) != 0,
- dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
+ (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
}
private void updateSystemUi(IBinder token, int vis, int backDisposition) {
@@ -2322,6 +2260,22 @@
}
}
+ @BinderThread
+ @Override
+ public void reportStartInput(IBinder token, IBinder startInputToken) {
+ if (!calledWithValidToken(token)) {
+ return;
+ }
+
+ synchronized (mMethodMap) {
+ final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
+ if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+ mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
+ }
+ mLastImeTargetWindow = targetWindow;
+ }
+ }
+
// Caution! This method is called in this class. Handle multi-user carefully
private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
if (!calledWithValidToken(token)) {
@@ -2732,15 +2686,13 @@
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
int unverifiedTargetSdkVersion) {
- final InputBindResult result;
- if (windowToken != null) {
- result = windowGainedFocus(startInputReason, client, windowToken, controlFlags,
- softInputMode, windowFlags, attribute, inputContext, missingMethods,
- unverifiedTargetSdkVersion);
- } else {
- result = startInput(startInputReason, client, inputContext, missingMethods, attribute,
- controlFlags);
+ if (windowToken == null) {
+ Slog.e(TAG, "windowToken cannot be null.");
+ return InputBindResult.NULL;
}
+ final InputBindResult result = startInputOrWindowGainedFocusInternal(startInputReason,
+ client, windowToken, controlFlags, softInputMode, windowFlags, attribute,
+ inputContext, missingMethods, unverifiedTargetSdkVersion);
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
@@ -2753,9 +2705,9 @@
}
@NonNull
- private InputBindResult windowGainedFocus(
+ private InputBindResult startInputOrWindowGainedFocusInternal(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IBinder windowToken, int controlFlags,
+ IInputMethodClient client, @NonNull IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, EditorInfo attribute, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@@ -2766,7 +2718,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
- if (DEBUG) Slog.v(TAG, "windowGainedFocus: reason="
+ if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
+ InputMethodClient.getStartInputReason(startInputReason)
+ " client=" + client.asBinder()
+ " inputContext=" + inputContext
@@ -4587,8 +4539,8 @@
InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode)
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
- p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
- + " mBoundToMethod=" + mBoundToMethod);
+ p.println(" mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
+ + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + mVisibleBound);
p.println(" mCurToken=" + mCurToken);
p.println(" mCurIntent=" + mCurIntent);
method = mCurMethod;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2deb759..92005d2 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -24,22 +24,18 @@
import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
-
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
@@ -125,9 +121,8 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.server.pm.PackageManagerService;
import com.android.server.storage.AppFuseBridge;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -163,6 +158,9 @@
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
/**
* Service responsible for various storage media. Connects to {@code vold} to
* watch for and manage dynamically added storage, such as SD cards and USB mass
@@ -1511,7 +1509,7 @@
}
private void systemReady() {
- LocalServices.getService(ActivityManagerInternal.class)
+ LocalServices.getService(ActivityTaskManagerInternal.class)
.registerScreenObserver(this);
mSystemReady = true;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 607db4e..ad9fa40 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1588,10 +1588,10 @@
// Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ // Create a version of the intent with the number always populated.
Intent intentWithPhoneNumber = new Intent(intent);
- if (!TextUtils.isEmpty(incomingNumber)) {
- intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
- }
+ intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
+
// Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
// that have the runtime one
mContext.sendBroadcastAsUser(intentWithPhoneNumber, UserHandle.ALL,
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index f24d8cd..c043e18 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -57,20 +57,20 @@
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.SpellCheckerSubtype;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.function.Predicate;
public class TextServicesManagerService extends ITextServicesManager.Stub {
@@ -251,7 +251,6 @@
for (int j = 0; j < numOnGoingSessionRequests; j++) {
final SessionRequest req = grp.mOnGoingSessionRequests.get(j);
pw.println(" " + "On going Request #" + j + ":");
- ++j;
pw.println(" " + "mTsListener=" + req.mTsListener);
pw.println(" " + "mScListener=" + req.mScListener);
pw.println(
@@ -510,7 +509,7 @@
// TODO: Save SpellCheckerSubtype by supported languages by looking at "locale".
@Override
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
- String locale, boolean allowImplicitlySelectedSubtype) {
+ boolean allowImplicitlySelectedSubtype) {
final int subtypeHashCode;
final SpellCheckerInfo sci;
final Locale systemLocale;
@@ -538,53 +537,39 @@
&& !allowImplicitlySelectedSubtype) {
return null;
}
- String candidateLocale = null;
- if (subtypeHashCode == 0) {
- // Spell checker language settings == "auto"
- final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- if (imm != null) {
- final InputMethodSubtype currentInputMethodSubtype =
- imm.getCurrentInputMethodSubtype();
- if (currentInputMethodSubtype != null) {
- final String localeString = currentInputMethodSubtype.getLocale();
- if (!TextUtils.isEmpty(localeString)) {
- // 1. Use keyboard locale if available in the spell checker
- candidateLocale = localeString;
- }
+
+ final int numSubtypes = sci.getSubtypeCount();
+ if (subtypeHashCode != 0) {
+ // Use the user specified spell checker subtype
+ for (int i = 0; i < numSubtypes; ++i) {
+ final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
+ if (scs.hashCode() == subtypeHashCode) {
+ return scs;
}
}
- if (candidateLocale == null) {
- // 2. Use System locale if available in the spell checker
- candidateLocale = systemLocale.toString();
- }
+ return null;
}
- SpellCheckerSubtype candidate = null;
+
+ // subtypeHashCode == 0 means spell checker language settings is "auto"
+
+ if (systemLocale == null) {
+ return null;
+ }
+ SpellCheckerSubtype firstLanguageMatchingSubtype = null;
for (int i = 0; i < sci.getSubtypeCount(); ++i) {
final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
- if (subtypeHashCode == 0) {
- final String scsLocale = scs.getLocale();
- if (candidateLocale.equals(scsLocale)) {
- return scs;
- } else if (candidate == null) {
- if (candidateLocale.length() >= 2 && scsLocale.length() >= 2
- && candidateLocale.startsWith(scsLocale)) {
- // Fall back to the applicable language
- candidate = scs;
- }
- }
- } else if (scs.hashCode() == subtypeHashCode) {
- if (DBG) {
- Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale
- + ", " + scs.getLocale());
- }
- // 3. Use the user specified spell check language
+ final Locale scsLocale = scs.getLocaleObject();
+ if (Objects.equals(scsLocale, systemLocale)) {
+ // Exact match wins.
return scs;
}
+ if (firstLanguageMatchingSubtype == null && scsLocale != null
+ && TextUtils.equals(systemLocale.getLanguage(), scsLocale.getLanguage())) {
+ // Remember as a fall back candidate
+ firstLanguageMatchingSubtype = scs;
+ }
}
- // 4. Fall back to the applicable language and return it if not null
- // 5. Simply just return it even if it's null which means we could find no suitable
- // spell check languages
- return candidate;
+ return firstLanguageMatchingSubtype;
}
@Override
@@ -1040,19 +1025,46 @@
private static final class ISpellCheckerServiceCallbackBinder
extends ISpellCheckerServiceCallback.Stub {
@NonNull
- private final SpellCheckerBindGroup mBindGroup;
- @NonNull
- private final SessionRequest mRequest;
+ private final Object mCallbackLock = new Object();
- ISpellCheckerServiceCallbackBinder(@NonNull final SpellCheckerBindGroup bindGroup,
- @NonNull final SessionRequest request) {
- mBindGroup = bindGroup;
- mRequest = request;
+ @GuardedBy("mCallbackLock")
+ @Nullable
+ private WeakReference<SpellCheckerBindGroup> mBindGroup;
+
+ /**
+ * Original {@link SessionRequest} that is associated with this callback.
+ *
+ * <p>Note that {@link SpellCheckerBindGroup#mOnGoingSessionRequests} guarantees that this
+ * {@link SessionRequest} object is kept alive until the request is canceled.</p>
+ */
+ @GuardedBy("mCallbackLock")
+ @Nullable
+ private WeakReference<SessionRequest> mRequest;
+
+ ISpellCheckerServiceCallbackBinder(@NonNull SpellCheckerBindGroup bindGroup,
+ @NonNull SessionRequest request) {
+ synchronized (mCallbackLock) {
+ mBindGroup = new WeakReference<>(bindGroup);
+ mRequest = new WeakReference<>(request);
+ }
}
@Override
public void onSessionCreated(@Nullable ISpellCheckerSession newSession) {
- mBindGroup.onSessionCreated(newSession, mRequest);
+ final SpellCheckerBindGroup group;
+ final SessionRequest request;
+ synchronized (mCallbackLock) {
+ if (mBindGroup == null || mRequest == null) {
+ return;
+ }
+ group = mBindGroup.get();
+ request = mRequest.get();
+ mBindGroup = null;
+ mRequest = null;
+ }
+ if (group != null && request != null) {
+ group.onSessionCreated(newSession, request);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ca715b5..f7cd5ad 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,7 +24,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
@@ -32,6 +31,7 @@
import java.util.Set;
import java.util.function.Predicate;
+import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -59,7 +59,6 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
-import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.server.AppStateTracker;
@@ -1390,7 +1389,7 @@
private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
ConnectionRecord modCr, boolean updateLru) {
if (modCr != null && modCr.binding.client != null) {
- if (modCr.binding.client.activities.size() <= 0) {
+ if (!modCr.binding.client.hasActivities()) {
// This connection is from a client without activities, so adding
// and removing is not interesting.
return false;
@@ -1408,7 +1407,7 @@
// Binding to ourself is not interesting.
continue;
}
- if (cr.binding.client.activities.size() > 0) {
+ if (cr.binding.client.hasActivities()) {
anyClientActivities = true;
break;
}
@@ -1869,7 +1868,7 @@
+ " type=" + resolvedType + " callingUid=" + callingUid);
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
+ ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", null);
ServiceMap smap = getServiceMapLocked(userId);
final ComponentName comp = service.getComponent();
@@ -2082,7 +2081,7 @@
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
- r.app.repProcState);
+ r.app.getReportedProcState());
if (!rebind) {
i.requested = true;
}
@@ -2464,7 +2463,7 @@
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
- app.repProcState);
+ app.getReportedProcState());
r.postNotification();
created = true;
} catch (DeadObjectException e) {
@@ -3051,7 +3050,7 @@
}
}
if (finishing) {
- if (r.app != null && !r.app.persistent) {
+ if (r.app != null && !r.app.isPersistent()) {
r.app.services.remove(r);
if (r.whitelistManager) {
updateWhitelistManagerLocked(r.app);
@@ -3141,7 +3140,7 @@
&& (filterByClasses == null
|| filterByClasses.contains(service.name.getClassName())));
if (sameComponent
- && (service.app == null || evenPersistent || !service.app.persistent)) {
+ && (service.app == null || evenPersistent || !service.app.isPersistent())) {
if (!doit) {
return true;
}
@@ -3149,7 +3148,7 @@
Slog.i(TAG, " Force stopping service " + service);
if (service.app != null) {
service.app.removed = killProcess;
- if (!service.app.persistent) {
+ if (!service.app.isPersistent()) {
service.app.services.remove(service);
if (service.whitelistManager) {
updateWhitelistManagerLocked(service.app);
@@ -3303,7 +3302,7 @@
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
- if (sr.app != app && sr.app != null && !sr.app.persistent) {
+ if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
sr.app.services.remove(sr);
}
sr.app = null;
@@ -3348,7 +3347,7 @@
continue;
}
// XXX turned off for now until we have more time to get a better policy.
- if (false && proc != null && !proc.persistent && proc.thread != null
+ if (false && proc != null && !proc.isPersistent() && proc.thread != null
&& proc.pid != 0 && proc.pid != ActivityManagerService.MY_PID
&& proc.setProcState >= ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
proc.kill("bound to service " + sr.name.flattenToShortString()
@@ -3366,7 +3365,7 @@
// Unless the process is persistent, this process record is going away,
// so make sure the service is cleaned out of it.
- if (!app.persistent) {
+ if (!app.isPersistent()) {
app.services.removeAt(i);
}
@@ -3476,7 +3475,7 @@
if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
}
- if (r.app != null && r.app.persistent) {
+ if (r.app != null && r.app.isPersistent()) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 698b6f7..9f9fe4c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -30,16 +30,15 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.ID;
+import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityDisplayProto.STACKS;
-import static com.android.server.am.ActivityDisplayProto.ID;
import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.graphics.Point;
@@ -47,11 +46,13 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ConfigurationContainer;
import com.android.server.wm.DisplayWindowController;
-
import com.android.server.wm.WindowContainerListener;
+
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -90,9 +91,9 @@
private IntArray mDisplayAccessUIDs = new IntArray();
/** All tokens used to put activities on this stack to sleep (including mOffToken) */
- final ArrayList<ActivityManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
+ final ArrayList<ActivityTaskManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>();
/** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
- ActivityManagerInternal.SleepToken mOffToken;
+ ActivityTaskManagerInternal.SleepToken mOffToken;
private boolean mSleeping;
@@ -140,7 +141,7 @@
+ " to displayId=" + mDisplayId + " position=" + position);
addStackReferenceIfNeeded(stack);
positionChildAt(stack, position);
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mSupervisor.mService.mAm.updateSleepIfNeededLocked();
}
void removeChild(ActivityStack stack) {
@@ -148,7 +149,7 @@
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
removeStackReferenceIfNeeded(stack);
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mSupervisor.mService.mAm.updateSleepIfNeededLocked();
onStackOrderChanged();
}
@@ -173,12 +174,22 @@
private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
int position = mStacks.size();
- if (position > 0) {
- final ActivityStack topStack = mStacks.get(position - 1);
- if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) {
- // If the top stack is always on top, we move this stack just below it.
- position--;
+ if (stack.inPinnedWindowingMode()) {
+ // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
+ // just return the candidate position.
+ return Math.min(position, candidatePosition);
+ }
+ while (position > 0) {
+ final ActivityStack targetStack = mStacks.get(position - 1);
+ if (!targetStack.isAlwaysOnTop()) {
+ // We reached a stack that isn't always-on-top.
+ break;
}
+ if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
+ // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
+ break;
+ }
+ position--;
}
return Math.min(position, candidatePosition);
}
@@ -290,7 +301,7 @@
}
}
- final ActivityManagerService service = mSupervisor.mService;
+ final ActivityTaskManagerService service = mSupervisor.mService;
if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
service.mSupportsPictureInPicture, activityType)) {
@@ -526,7 +537,7 @@
}
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- final ActivityManagerService service = mSupervisor.mService;
+ final ActivityTaskManagerService service = mSupervisor.mService;
boolean supportsMultiWindow = service.mSupportsMultiWindow;
boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
@@ -704,7 +715,7 @@
boolean shouldSleep() {
return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mSupervisor.mService.mRunningVoice == null);
+ && (mSupervisor.mService.mAm.mRunningVoice == null);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 97c3fe4..70901d0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,43 +16,22 @@
package com.android.server.am;
-import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
-import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
-import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.Manifest.permission.STOP_APP_SWITCHES;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.app.AppOpsManager.OP_NONE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -61,10 +40,8 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
-import static android.os.Build.VERSION_CODES.N;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -86,7 +63,6 @@
import static android.os.Process.SCHED_RESET_ON_FORK;
import static android.os.Process.SE_UID;
import static android.os.Process.SHELL_UID;
-import static android.os.Process.SIGNAL_QUIT;
import static android.os.Process.SIGNAL_USR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
@@ -113,17 +89,12 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.provider.Settings.System.FONT_SCALE;
-import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -139,8 +110,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
@@ -154,20 +123,15 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
@@ -178,35 +142,15 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -219,13 +163,9 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.ScreenObserver;
-import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerProto;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AppGlobals;
@@ -253,22 +193,14 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
-import android.app.RemoteAction;
-import android.app.WaitResult;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
-import android.app.admin.DevicePolicyCache;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
import android.app.backup.IBackupManager;
-import android.app.servertransaction.ConfigurationChangeItem;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ComponentCallbacks2;
@@ -305,13 +237,10 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
import android.location.LocationManager;
import android.media.audiofx.AudioEffect;
-import android.metrics.LogMaker;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
@@ -323,7 +252,6 @@
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.FactoryTest;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -331,12 +259,10 @@
import android.os.IPermissionController;
import android.os.IProcessInfoService;
import android.os.IProgressListener;
-import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
@@ -352,7 +278,6 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.TransactionTooLargeException;
-import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
@@ -362,11 +287,8 @@
import android.provider.Downloads;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionManagerInternal;
-import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
-import android.text.format.Time;
import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -389,16 +311,16 @@
import android.view.Gravity;
import android.view.IRecentsAnimationRunner;
import android.view.LayoutInflater;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationDefinition;
import android.view.View;
import android.view.WindowManager;
import android.view.autofill.AutofillManagerInternal;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.AssistUtils;
import com.android.internal.app.DumpHeapActivity;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
@@ -406,21 +328,16 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BinderInternal;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -445,17 +362,17 @@
import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.am.ActivityManagerServiceProto;
import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import com.android.server.am.ActivityManagerServiceDumpServicesProto;
+import com.android.server.am.ActivityManagerServiceProto;
+import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.GrantUriProto;
import com.android.server.am.ImportanceTokenProto;
import com.android.server.am.MemInfoDumpProto;
+import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.am.NeededUriGrantsProto;
import com.android.server.am.ProcessOomProto;
import com.android.server.am.ProcessToGcProto;
@@ -467,17 +384,10 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.utils.PriorityDump;
import com.android.server.vr.VrManagerInternal;
-import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.WindowManagerService;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -514,6 +424,10 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -527,8 +441,6 @@
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
- private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
- private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
private static final String TAG_LRU = TAG + POSTFIX_LRU;
private static final String TAG_MU = TAG + POSTFIX_MU;
@@ -539,13 +451,10 @@
private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
private static final String TAG_PSS = TAG + POSTFIX_PSS;
- private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
- private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
- private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
// Mock "pretend we're idle now" broadcast action to the job scheduler; declared
// here so that while the job scheduler can depend on AMS, the other way around
@@ -570,10 +479,6 @@
// Maximum number of receivers an app can register.
private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
- // Amount of time after a call to stopAppSwitches() during which we will
- // prevent further untrusted switches from happening.
- static final long APP_SWITCH_DELAY_TIME = 5*1000;
-
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
@@ -617,11 +522,6 @@
/** If a UID observer takes more than this long, send a WTF. */
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
- // Access modes for handleIncomingUser.
- static final int ALLOW_NON_FULL = 0;
- static final int ALLOW_NON_FULL_IN_PROFILE = 1;
- static final int ALLOW_FULL_ONLY = 2;
-
// Necessary ApplicationInfo flags to mark an app as persistent
private static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -633,9 +533,6 @@
// Used to indicate that an app transition should be animated.
static final boolean ANIMATE = true;
- // Determines whether to take full screen screenshots
- static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
-
/**
* Default value for {@link Settings.Global#NETWORK_ACCESS_TIMEOUT_MS}.
*/
@@ -668,21 +565,10 @@
/** All system services */
SystemServiceManager mSystemServiceManager;
- // Keeps track of the active voice interaction service component, notified from
- // VoiceInteractionManagerService
- ComponentName mActiveVoiceInteractionServiceComponent;
-
private Installer mInstaller;
/** Run all ActivityStacks through this */
- final ActivityStackSupervisor mStackSupervisor;
- final KeyguardController mKeyguardController;
-
- private final ActivityStartController mActivityStartController;
-
- private final ClientLifecycleManager mLifecycleManager;
-
- final TaskChangeNotificationController mTaskChangeNotificationController;
+ ActivityStackSupervisor mStackSupervisor;
final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
@@ -695,11 +581,6 @@
// devices.
private boolean mShowDialogs = true;
- private final VrController mVrController;
-
- // VR Vr2d Display Id.
- int mVr2dDisplayId = INVALID_DISPLAY;
-
// Whether we should use SCHED_FIFO for UI and RenderThreads.
private boolean mUseFifoUiScheduling = false;
@@ -742,28 +623,12 @@
private AppTimeTracker mCurAppTimeTracker;
/**
- * List of intents that were used to start the most recent tasks.
- */
- private final RecentTasks mRecentTasks;
-
- /**
* The package name of the DeviceOwner. This package is not permitted to have its data cleared.
*/
String mDeviceOwnerName;
- /**
- * The controller for all operations related to locktask.
- */
- private final LockTaskController mLockTaskController;
-
final UserController mUserController;
- /**
- * Packages that are being allowed to perform unrestricted app switches. Mapping is
- * User -> Type -> uid.
- */
- final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
-
final AppErrors mAppErrors;
final AppWarnings mAppWarnings;
@@ -812,7 +677,7 @@
public boolean canShowErrorDialogs() {
return mShowDialogs && !mSleeping && !mShuttingDown
- && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
+ && !mActivityTaskManager.mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
&& !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
mUserController.getCurrentUserId())
&& !(UserManager.isDeviceInDemoMode(mContext)
@@ -841,7 +706,22 @@
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
- final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
+ final MyProcessMap mProcessNames = new MyProcessMap();
+ final class MyProcessMap extends ProcessMap<ProcessRecord> {
+ @Override
+ public ProcessRecord put(String name, int uid, ProcessRecord value) {
+ final ProcessRecord r = super.put(name, uid, value);
+ mAtmInternal.onProcessAdded(r.getWindowProcessController());
+ return r;
+ }
+
+ @Override
+ public ProcessRecord remove(String name, int uid) {
+ final ProcessRecord r = super.remove(name, uid);
+ mAtmInternal.onProcessRemoved(name, uid);
+ return r;
+ }
+ }
/**
* Tracking long-term execution of processes to look for abuse and other
@@ -852,7 +732,7 @@
/**
* The currently running isolated processes.
*/
- final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
+ final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
/**
* Counter for assigning isolated process uids, to avoid frequently reusing the
@@ -979,23 +859,6 @@
boolean mFullPssPending = false;
/**
- * This is the process holding what we currently consider to be
- * the "home" activity.
- */
- ProcessRecord mHomeProcess;
-
- /**
- * This is the process holding the activity the user last visited that
- * is in a different process from the one they are currently in.
- */
- ProcessRecord mPreviousProcess;
-
- /**
- * The time at which the previous process was last visible.
- */
- long mPreviousProcessVisibleTime;
-
- /**
* Track all uids that have actively running processes.
*/
final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
@@ -1364,16 +1227,6 @@
*/
final AppOpsService mAppOpsService;
- /** Current sequencing integer of the configuration, for skipping old configurations. */
- private int mConfigurationSeq;
-
- /**
- * Temp object used when global and/or display override configuration is updated. It is also
- * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
- * anyone...
- */
- private Configuration mTempConfig = new Configuration();
-
/**
* Hardware-reported OpenGLES version.
*/
@@ -1418,18 +1271,6 @@
final Context mUiContext;
/**
- * The time at which we will allow normal application switches again,
- * after a call to {@link #stopAppSwitches()}.
- */
- long mAppSwitchesAllowedTime;
-
- /**
- * This is set to true after the first switch after mAppSwitchesAllowedTime
- * is set; any switches after that will clear the time.
- */
- boolean mDidAppSwitch;
-
- /**
* Last time (in uptime) at which we checked for power usage.
*/
long mLastPowerCheckUptime;
@@ -1580,29 +1421,7 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- boolean mForceResizableActivities;
- /**
- * Flag that indicates if multi-window is enabled.
- *
- * For any particular form of multi-window to be enabled, generic multi-window must be enabled
- * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
- * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
- * At least one of the forms of multi-window must be enabled in order for this flag to be
- * initialized to 'true'.
- *
- * @see #mSupportsSplitScreenMultiWindow
- * @see #mSupportsFreeformWindowManagement
- * @see #mSupportsPictureInPicture
- * @see #mSupportsMultiDisplay
- */
- boolean mSupportsMultiWindow;
- boolean mSupportsSplitScreenMultiWindow;
- boolean mSupportsFreeformWindowManagement;
- boolean mSupportsPictureInPicture;
- boolean mSupportsMultiDisplay;
- boolean mSupportsLeanbackOnly;
- IActivityController mController = null;
- boolean mControllerIsAMonkey = false;
+
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
@@ -1726,8 +1545,6 @@
}
}
- final List<ScreenObserver> mScreenObservers = new ArrayList<>();
-
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1769,12 +1586,6 @@
long mLastWriteTime = 0;
/**
- * Used to retain an update lock when the foreground activity is in
- * immersive mode.
- */
- final UpdateLock mUpdateLock = new UpdateLock("immersive");
-
- /**
* Set to true after the system has finished booting.
*/
boolean mBooted = false;
@@ -1786,6 +1597,7 @@
WindowManagerService mWindowManager;
ActivityTaskManagerService mActivityTaskManager;
+ ActivityTaskManagerInternal mAtmInternal;
final ActivityThread mSystemThread;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1837,10 +1649,8 @@
static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
- static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
- static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
static final int DISMISS_DIALOG_UI_MSG = 48;
@@ -1852,11 +1662,7 @@
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
static final int IDLE_UIDS_MSG = 58;
- static final int LOG_STACK_STATE = 60;
- static final int VR_MODE_CHANGE_MSG = 61;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
- static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
- static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
@@ -1882,11 +1688,6 @@
*/
private boolean mUserIsMonkey;
- /** The dimensions of the thumbnails in the Recents UI. */
- int mThumbnailWidth;
- int mThumbnailHeight;
- float mFullscreenThumbnailScale;
-
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final Handler mUiHandler;
@@ -2130,6 +1931,9 @@
}
}
callbacks.finishBroadcast();
+ // We have to clean up the RemoteCallbackList here, because otherwise it will
+ // needlessly hold the enclosed callbacks until the remote process dies.
+ callbacks.kill();
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
@@ -2220,13 +2024,13 @@
}
ActivityRecord root = (ActivityRecord)msg.obj;
- ProcessRecord process = root.app;
+ final WindowProcessController process = root.app;
if (process == null) {
return;
}
try {
- Context context = mContext.createPackageContext(process.info.packageName, 0);
+ Context context = mContext.createPackageContext(process.mInfo.packageName, 0);
String text = mContext.getString(R.string.heavy_weight_notification,
context.getApplicationInfo().loadLabel(context.getPackageManager()));
Notification notification =
@@ -2290,20 +2094,6 @@
thread.start();
break;
}
- case IMMERSIVE_MODE_LOCK_MSG: {
- final boolean nextState = (msg.arg1 != 0);
- if (mUpdateLock.isHeld() != nextState) {
- if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
- "Applying new update lock state '" + nextState
- + "' for " + (ActivityRecord)msg.obj);
- if (nextState) {
- mUpdateLock.acquire();
- } else {
- mUpdateLock.release();
- }
- }
- break;
- }
case PERSIST_URI_GRANTS_MSG: {
writeGrantedUriPermissions();
break;
@@ -2326,18 +2116,6 @@
}
break;
}
- case ENTER_ANIMATION_COMPLETE_MSG: {
- synchronized (ActivityManagerService.this) {
- ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
- if (r != null && r.app != null && r.app.thread != null) {
- try {
- r.app.thread.scheduleEnterAnimationComplete(r.appToken);
- } catch (RemoteException e) {
- }
- }
- }
- break;
- }
case FINISH_BOOTING_MSG: {
if (msg.arg1 != 0) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
@@ -2483,32 +2261,6 @@
case IDLE_UIDS_MSG: {
idleUids();
} break;
- case VR_MODE_CHANGE_MSG: {
- if (!mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
- return;
- }
- synchronized (ActivityManagerService.this) {
- final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
- mWindowManager.disableNonVrUi(disableNonVrUi);
- if (disableNonVrUi) {
- // If we are in a VR mode where Picture-in-Picture mode is unsupported,
- // then remove the pinned stack.
- mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
- }
- }
- } break;
- case DISPATCH_SCREEN_AWAKE_MSG: {
- final boolean isAwake = msg.arg1 != 0;
- for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
- mScreenObservers.get(i).onAwakeStateChanged(isAwake);
- }
- } break;
- case DISPATCH_SCREEN_KEYGUARD_MSG: {
- final boolean isShowing = msg.arg1 != 0;
- for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
- mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
- }
- } break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -2662,8 +2414,9 @@
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
- app.persistent = true;
+ app.setPersistent(true);
app.pid = MY_PID;
+ app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
@@ -2694,16 +2447,8 @@
public void setWindowManager(WindowManagerService wm) {
synchronized (this) {
mWindowManager = wm;
+ mActivityTaskManager.setWindowManager(wm);
mStackSupervisor.setWindowManager(wm);
- mLockTaskController.setWindowManager(wm);
- }
- }
-
- public void setActivityTaskManager(ActivityTaskManagerService atm) {
- synchronized (this) {
- mActivityTaskManager = atm;
- mStackSupervisor.setActivityTaskManager(atm);
- mActivityTaskManager.setActivityManagerService(this);
}
}
@@ -2807,10 +2552,17 @@
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
+ private static ActivityTaskManagerService sAtm;
public Lifecycle(Context context) {
super(context);
- mService = new ActivityManagerService(context);
+ mService = new ActivityManagerService(context, sAtm);
+ }
+
+ public static ActivityManagerService startService(
+ SystemServiceManager ssm, ActivityTaskManagerService atm) {
+ sAtm = atm;
+ return ssm.startService(ActivityManagerService.Lifecycle.class).getService();
}
@Override
@@ -2942,7 +2694,6 @@
mContext = mInjector.getContext();
mUiContext = null;
GL_ES_VERSION = 0;
- mActivityStartController = null;
mAppErrors = null;
mAppWarnings = null;
mAppOpsService = mInjector.getAppOpsService(null, null);
@@ -2953,21 +2704,14 @@
mHandler = null;
mHandlerThread = null;
mIntentFirewall = null;
- mKeyguardController = null;
mPermissionReviewRequired = false;
mProcessCpuThread = null;
mProcessStats = null;
mProviderMap = null;
- mRecentTasks = null;
mServices = null;
- mStackSupervisor = null;
mSystemThread = null;
- mTaskChangeNotificationController = null;
mUiHandler = injector.getUiHandler(null);
mUserController = null;
- mVrController = null;
- mLockTaskController = null;
- mLifecycleManager = null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
@@ -2975,7 +2719,7 @@
// Note: This method is invoked on the main thread but may need to attach various
// handlers to other threads. So take care to be explicit about the looper.
- public ActivityManagerService(Context systemContext) {
+ public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
mInjector = new Injector();
mContext = systemContext;
@@ -3043,8 +2787,6 @@
mUserController = new UserController(this);
- mVrController = new VrController(this);
-
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -3053,21 +2795,13 @@
}
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
- mTempConfig.setToDefaults();
- mTempConfig.setLocales(LocaleList.getDefault());
- mConfigurationSeq = mTempConfig.seq = 1;
- mStackSupervisor = createStackSupervisor();
- mStackSupervisor.onConfigurationChanged(mTempConfig);
- mKeyguardController = mStackSupervisor.getKeyguardController();
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
- mTaskChangeNotificationController =
- new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
- mActivityStartController = new ActivityStartController(this);
- mRecentTasks = createRecentTasks();
- mStackSupervisor.setRecentTasks(mRecentTasks);
- mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
- mLifecycleManager = new ClientLifecycleManager();
+
+ mActivityTaskManager = atm;
+ mActivityTaskManager.setActivityManagerService(this);
+ mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mStackSupervisor = mActivityTaskManager.mStackSupervisor;
mProcessCpuThread = new Thread("CpuTracker") {
@Override
@@ -3121,20 +2855,6 @@
}
- protected ActivityStackSupervisor createStackSupervisor() {
- final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mHandler.getLooper());
- supervisor.initialize();
- return supervisor;
- }
-
- protected RecentTasks createRecentTasks() {
- return new RecentTasks(this, mStackSupervisor);
- }
-
- RecentTasks getRecentTasks() {
- return mRecentTasks;
- }
-
public void setSystemServiceManager(SystemServiceManager mgr) {
mSystemServiceManager = mgr;
}
@@ -3151,8 +2871,9 @@
mAppOpsService.publish(mContext);
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+ mActivityTaskManager.onActivityManagerInternalAdded();
// Wait for the synchronized block started in mProcessCpuThread,
- // so that any other acccess to mProcessCpuTracker from main thread
+ // so that any other access to mProcessCpuTracker from main thread
// will be blocked during mProcessCpuTracker initialization.
try {
mProcessCpuInitLatch.await();
@@ -3163,11 +2884,6 @@
}
}
- void onUserStoppedLocked(int userId) {
- mRecentTasks.unloadUserDataFromMemoryLocked(userId);
- mAllowAppSwitchUids.remove(userId);
- }
-
public void initPowerManagement() {
mStackSupervisor.initPowerManagement();
mBatteryStatsService.initPowerManagement();
@@ -3490,8 +3206,8 @@
mWindowManager.setFocusedApp(r.appToken, true);
- applyUpdateLockStateLocked(r);
- applyUpdateVrModeLocked(r);
+ mActivityTaskManager.applyUpdateLockStateLocked(r);
+ mActivityTaskManager.applyUpdateVrModeLocked(r);
EventLogTags.writeAmSetResumedActivity(
r == null ? -1 : r.userId,
@@ -3534,36 +3250,6 @@
mActivityTaskManager.unregisterTaskStackListener(listener);
}
- final void applyUpdateLockStateLocked(ActivityRecord r) {
- // Modifications to the UpdateLock state are done on our handler, outside
- // the activity manager's locks. The new state is determined based on the
- // state *now* of the relevant activity record. The object is passed to
- // the handler solely for logging detail, not to be consulted/modified.
- final boolean nextState = r != null && r.immersive;
- mHandler.sendMessage(
- mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
- }
-
- final void applyUpdateVrModeLocked(ActivityRecord r) {
- // VR apps are expected to run in a main display. If an app is turning on VR for
- // itself, but lives in a dynamic stack, then make sure that it is moved to the main
- // fullscreen stack before enabling VR Mode.
- // TODO: The goal of this code is to keep the VR app on the main display. When the
- // stack implementation changes in the future, keep in mind that the use of the fullscreen
- // stack is a means to move the activity to the main display and a moveActivityToDisplay()
- // option would be a better choice here.
- if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
- Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
- + " to main stack for VR");
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
- mActivityTaskManager.moveTaskToStack(r.getTask().taskId, stack.mStackId,
- true /* toTop */);
- }
- mHandler.sendMessage(
- mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
- }
-
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -3590,7 +3276,7 @@
String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
- if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
+ if (app.hasActivitiesOrRecentTasks()) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
@@ -3637,7 +3323,7 @@
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui >= 0) {
if (!app.killed) {
- if (app.persistent) {
+ if (app.isPersistent()) {
Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
} else {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
@@ -3661,8 +3347,8 @@
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
- final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
- || app.treatLikeActivity || app.recentTasks.size() > 0;
+ final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities
+ || app.treatLikeActivity;
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// The process has activities, so we are only allowing activity-based adjustments
@@ -3694,7 +3380,7 @@
int lrui = mLruProcesses.lastIndexOf(app);
- if (app.persistent && lrui >= 0) {
+ if (app.isPersistent() && lrui >= 0) {
// We don't care about the position of persistent processes, as long as
// they are in the list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
@@ -3766,7 +3452,7 @@
int nextIndex;
if (hasActivity) {
final int N = mLruProcesses.size();
- if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
+ if ((!app.hasActivities() || app.hasRecentTasks())
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
@@ -3841,14 +3527,14 @@
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
- && !cr.binding.service.app.persistent) {
+ && !cr.binding.service.app.isPersistent()) {
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
}
}
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
- if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
+ if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
}
@@ -3878,7 +3564,7 @@
&& proc.lastCachedPss >= 4000) {
// Turn this condition on to cause killing to happen regularly, for testing.
if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+ proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge
@@ -3887,7 +3573,7 @@
if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+ proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
@@ -3901,13 +3587,6 @@
}
}
- boolean isNextTransitionForward() {
- int transit = mWindowManager.getPendingAppTransition();
- return transit == TRANSIT_ACTIVITY_OPEN
- || transit == TRANSIT_TASK_OPEN
- || transit == TRANSIT_TASK_TO_FRONT;
- }
-
boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
String processName, String abiOverride, int uid, Runnable crashHandler) {
synchronized(this) {
@@ -4200,7 +3879,7 @@
}
if (app.info.isPrivilegedApp() &&
- DexManager.isPackageSelectedToRunOob(app.pkgList.keySet())) {
+ DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
@@ -4242,7 +3921,7 @@
}
app.gids = gids;
- app.requiredAbi = requiredAbi;
+ app.setRequiredAbi(requiredAbi);
app.instructionSet = instructionSet;
// the per-user SELinux context must be set
@@ -4433,7 +4112,7 @@
// Ignore
}
- if (app.persistent) {
+ if (app.isPersistent()) {
Watchdog.getInstance().processStarted(app.processName, pid);
}
@@ -4492,7 +4171,7 @@
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- component.app.uid, component.realActivity.getPackageName(),
+ component.app.mUid, component.realActivity.getPackageName(),
component.realActivity.getShortClassName(), resumed ?
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
@@ -4503,7 +4182,7 @@
}
synchronized (stats) {
- stats.noteActivityResumedLocked(component.app.uid);
+ stats.noteActivityResumedLocked(component.app.mUid);
}
} else {
if (mUsageStatsService != null) {
@@ -4511,7 +4190,7 @@
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
- stats.noteActivityPausedLocked(component.app.uid);
+ stats.noteActivityPausedLocked(component.app.mUid);
}
}
}
@@ -4550,7 +4229,7 @@
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
- mActivityStartController.startHomeActivity(intent, aInfo, myReason);
+ mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo, myReason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
@@ -4595,7 +4274,7 @@
return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
}
- void enforceNotIsolatedCaller(String caller) {
+ private void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
@@ -4980,7 +4659,7 @@
// Inform the activity
try {
- activityToCallback.app.thread.scheduleLocalVoiceInteractionStarted(activity,
+ activityToCallback.app.getThread().scheduleLocalVoiceInteractionStarted(activity,
voiceInteractor);
long token = Binder.clearCallingIdentity();
try {
@@ -5048,14 +4727,7 @@
return;
}
- ArrayList<ActivityRecord> activities = new ArrayList<>(proc.activities);
- for (int i = 0; i < activities.size(); i++) {
- ActivityRecord r = activities.get(i);
- if (!r.finishing && r.isInStackLocked()) {
- r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
- null, "finish-heavy", true);
- }
- }
+ proc.getWindowProcessController().finishActivities();
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
proc.userId, 0));
@@ -5104,11 +4776,10 @@
}
// Remove this application's activities from active lists.
- boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
+ boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app.getWindowProcessController());
app.clearRecentTasks();
-
- app.activities.clear();
+ app.clearActivities();
if (app.instr != null) {
Slog.w(TAG, "Crash of app " + app.processName
@@ -5181,7 +4852,7 @@
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec.thread != null
- && rec.setProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
haveBg = true;
break;
}
@@ -5433,7 +5104,7 @@
return SystemClock.elapsedRealtime() - timeStart;
}
- private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
+ public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
// We don't need any sort of inotify based monitoring when we're dumping traces via
@@ -5509,90 +5180,6 @@
}
}
- final void logAppTooSlow(ProcessRecord app, long startTime, String msg) {
- if (true || Build.IS_USER) {
- return;
- }
-
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- StrictMode.allowThreadDiskWrites();
- try {
- File tracesDir = new File("/data/anr");
- File tracesFile = null;
- try {
- tracesFile = File.createTempFile("app_slow", null, tracesDir);
-
- StringBuilder sb = new StringBuilder();
- Time tobj = new Time();
- tobj.set(System.currentTimeMillis());
- sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
- sb.append(": ");
- TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
- sb.append(" since ");
- sb.append(msg);
- FileOutputStream fos = new FileOutputStream(tracesFile);
- fos.write(sb.toString().getBytes());
- if (app == null) {
- fos.write("\n*** No application process!".getBytes());
- }
- fos.close();
- FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
- } catch (IOException e) {
- Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
- return;
- }
-
- if (app != null && app.pid > 0) {
- ArrayList<Integer> firstPids = new ArrayList<Integer>();
- firstPids.add(app.pid);
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
- }
-
- File lastTracesFile = null;
- File curTracesFile = null;
- for (int i=9; i>=0; i--) {
- String name = String.format(Locale.US, "slow%02d.txt", i);
- curTracesFile = new File(tracesDir, name);
- if (curTracesFile.exists()) {
- if (lastTracesFile != null) {
- curTracesFile.renameTo(lastTracesFile);
- } else {
- curTracesFile.delete();
- }
- }
- lastTracesFile = curTracesFile;
- }
- tracesFile.renameTo(curTracesFile);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
-
- @GuardedBy("this")
- final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
- if (!mLaunchWarningShown) {
- mLaunchWarningShown = true;
- mUiHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (ActivityManagerService.this) {
- final Dialog d = new LaunchWarningWindow(mContext, cur, next);
- d.show();
- mUiHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- synchronized (ActivityManagerService.this) {
- d.dismiss();
- mLaunchWarningShown = false;
- }
- }
- }, 4000);
- }
- }
- });
- }
- }
-
@Override
public boolean clearApplicationUserData(final String packageName, boolean keepState,
final IPackageDataObserver observer, int userId) {
@@ -5659,7 +5246,7 @@
if (appInfo != null) {
forceStopPackageLocked(packageName, appInfo.uid, "clear data");
- mRecentTasks.removeTasksByPackageName(packageName, resolvedUserId);
+ mActivityTaskManager.getRecentTasks().removeTasksByPackageName(packageName, resolvedUserId);
}
}
@@ -5793,7 +5380,7 @@
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
- if (app.persistent) {
+ if (app.isPersistent()) {
// We don't kill persistent processes.
continue;
}
@@ -5830,7 +5417,7 @@
* @param maxProcState the process state at or below which to preserve
* processes, or {@code -1} to ignore the process state
*/
- private void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+ void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: killAllBackgroundProcessesExcept() from pid="
@@ -6048,7 +5635,7 @@
proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
infos[i].getTotalUss(), infos[i].getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
- proc.pkgList);
+ proc.pkgList.mPkgList);
}
}
}
@@ -6078,7 +5665,7 @@
if (proc.thread != null && proc.setAdj == oomAdj) {
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
- ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList);
+ ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList.mPkgList);
}
}
}
@@ -6151,7 +5738,7 @@
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
- if (app.persistent && !evenPersistent) {
+ if (app.isPersistent() && !evenPersistent) {
// we don't kill persistent processes
continue;
}
@@ -6277,7 +5864,7 @@
}
// Clean-up disabled tasks
- mRecentTasks.cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
+ mActivityTaskManager.getRecentTasks().cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
// Clean-up disabled services.
mServices.bringDownDisabledPackageServicesLocked(
@@ -6341,7 +5928,7 @@
ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
packageName == null ? ("stop user " + userId) : ("stop " + packageName));
- didSomething |= mActivityStartController.clearPendingActivityLaunches(packageName);
+ didSomething |= mActivityTaskManager.getActivityStartController().clearPendingActivityLaunches(packageName);
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
packageName, null, doit, evenPersistent, userId)) {
@@ -6488,7 +6075,7 @@
// We shouldn't already have a process under this name, but just in case we
// need to clean up whatever may be there now.
ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
- if (old == proc && proc.persistent) {
+ if (old == proc && proc.isPersistent()) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
@@ -6555,7 +6142,7 @@
}
}
boolean willRestart = false;
- if (app.persistent && !app.isolated) {
+ if (app.isPersistent() && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
@@ -6708,7 +6295,7 @@
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
- app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
@@ -6887,7 +6474,7 @@
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.persistent,
+ isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -6896,7 +6483,7 @@
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.persistent,
+ isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -7030,13 +6617,6 @@
mWindowManager.showBootMessage(msg, always);
}
- /**
- * @return whther the keyguard is currently locked.
- */
- boolean isKeyguardLocked() {
- return mKeyguardController.isKeyguardLocked();
- }
-
final void finishBooting() {
synchronized (this) {
if (!mBootAnimationComplete) {
@@ -7798,7 +7378,7 @@
}
}
- int checkComponentPermission(String permission, int pid, int uid,
+ static int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -7886,15 +7466,6 @@
}
/**
- * This can be called with or without the global lock held.
- */
- void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
- if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
- enforceCallingPermission(permission, func);
- }
- }
-
- /**
* Determine if UID is holding permissions required to access {@link Uri} in
* the given {@link ProviderInfo}. Final permission checking is always done
* in {@link ContentProvider}.
@@ -9351,38 +8922,6 @@
mActivityTaskManager.cancelTaskWindowTransition(taskId);
}
- boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
- if (mRecentTasks.isCallerRecents(callingUid)) {
- // Always allow the recents component to get tasks
- return true;
- }
-
- boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
- if (!allowed) {
- if (checkPermission(android.Manifest.permission.GET_TASKS,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
- // Temporary compatibility: some existing apps on the system image may
- // still be requesting the old permission and not switched to the new
- // one; if so, we'll still allow them full access. This means we need
- // to see if they are holding the old permission and are a system app.
- try {
- if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
- allowed = true;
- if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
- + " is using old GET_TASKS but privileged; allowing");
- }
- } catch (RemoteException e) {
- }
- }
- }
- if (!allowed) {
- if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
- + " does not hold REAL_GET_TASKS; limiting output");
- }
- return allowed;
- }
-
@Override
public void setTaskResizeable(int taskId, int resizeableMode) {
mActivityTaskManager.setTaskResizeable(taskId, resizeableMode);
@@ -9518,7 +9057,7 @@
synchronized (this) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
Arrays.toString(packages));
- mLockTaskController.updateLockTaskPackages(userId, packages);
+ mActivityTaskManager.getLockTaskController().updateLockTaskPackages(userId, packages);
}
}
@@ -9807,7 +9346,7 @@
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
// The client is more important than last activity -- note the time this
// is happening, so we keep the old provider process around a bit as last
// activity to avoid thrashing it.
@@ -10290,18 +9829,6 @@
return AppGlobals.getPackageManager();
}
- ActivityStartController getActivityStartController() {
- return mActivityStartController;
- }
-
- LockTaskController getLockTaskController() {
- return mLockTaskController;
- }
-
- ClientLifecycleManager getLifecycleManager() {
- return mLifecycleManager;
- }
-
PackageManagerInternal getPackageManagerInternalLocked() {
if (mPackageManagerInt == null) {
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
@@ -10819,9 +10346,9 @@
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
- r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- r.persistent = true;
+ r.setPersistent(true);
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (isolated && isolatedUid != 0) {
@@ -10920,7 +10447,7 @@
}
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
- app.persistent = true;
+ app.setPersistent(true);
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
@@ -11011,8 +10538,7 @@
// Also update state in a special way for running foreground services UI.
mServices.updateScreenStateLocked(isAwake);
reportCurWakefulnessUsageEventLocked();
- mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
- .sendToTarget();
+ mActivityTaskManager.onScreenAwakeChanged(isAwake);
}
updateOomAdjLocked();
}
@@ -11065,11 +10591,6 @@
}
}
- /** Pokes the task persister. */
- void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
- mRecentTasks.notifyTaskPersisterLocked(task, flush);
- }
-
@Override
public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
@@ -11099,7 +10620,7 @@
mBatteryStatsService.shutdown();
synchronized (this) {
mProcessStats.shutdownLocked();
- notifyTaskPersisterLocked(null, true);
+ mActivityTaskManager.notifyTaskPersisterLocked(null, true);
}
return timedout;
@@ -11144,7 +10665,7 @@
final long ident = Binder.clearCallingIdentity();
try {
if (mUserController.shouldConfirmCredentials(userId)) {
- if (mKeyguardController.isKeyguardLocked()) {
+ if (mActivityTaskManager.mKeyguardController.isKeyguardLocked()) {
// Showing launcher to avoid user entering credential twice.
final int currentUserId = mUserController.getCurrentUserId();
startHomeActivityLocked(currentUserId, "notifyLockedProfile");
@@ -11181,69 +10702,12 @@
@Override
public void stopAppSwitches() {
- enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
- synchronized(this) {
- mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
- + APP_SWITCH_DELAY_TIME;
- mDidAppSwitch = false;
- mActivityStartController.schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
- }
+ mActivityTaskManager.stopAppSwitches();
}
+ @Override
public void resumeAppSwitches() {
- enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
- synchronized(this) {
- // Note that we don't execute any pending app switches... we will
- // let those wait until either the timeout, or the next start
- // activity request.
- mAppSwitchesAllowedTime = 0;
- }
- }
-
- boolean checkAllowAppSwitchUid(int uid) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
- if (types != null) {
- for (int i = types.size() - 1; i >= 0; i--) {
- if (types.valueAt(i).intValue() == uid) {
- return true;
- }
- }
- }
- return false;
- }
-
- boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
- int callingPid, int callingUid, String name) {
- if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
- return true;
- }
-
- if (mRecentTasks.isCallerRecents(sourceUid)) {
- return true;
- }
-
- int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
- if (perm == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (checkAllowAppSwitchUid(sourceUid)) {
- return true;
- }
-
- // If the actual IPC caller is different from the logical source, then
- // also see if they are allowed to control app switches.
- if (callingUid != -1 && callingUid != sourceUid) {
- perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
- if (perm == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (checkAllowAppSwitchUid(callingUid)) {
- return true;
- }
- }
-
- Slog.w(TAG, name + " request from " + sourceUid + " stopped");
- return false;
+ mActivityTaskManager.resumeAppSwitches();
}
public void setDebugApp(String packageName, boolean waitForDebugger,
@@ -11397,13 +10861,7 @@
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
- enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
- "setActivityController()");
- synchronized (this) {
- mController = controller;
- mControllerIsAMonkey = imAMonkey;
- Watchdog.getInstance().setActivityController(controller);
- }
+ mActivityTaskManager.setActivityController(controller, imAMonkey);
}
@Override
@@ -11428,7 +10886,7 @@
public boolean isUserAMonkey() {
synchronized (this) {
// If there is a controller also implies the user is a monkey.
- return (mUserIsMonkey || (mController != null && mControllerIsAMonkey));
+ return mUserIsMonkey || mActivityTaskManager.isControllerAMonkey();
}
}
@@ -11540,7 +10998,10 @@
public static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
- return r != null ? getInputDispatchingTimeoutLocked(r.app) : KEY_DISPATCHING_TIMEOUT;
+ if (r == null || !r.hasProcess()) {
+ return KEY_DISPATCHING_TIMEOUT;
+ }
+ return getInputDispatchingTimeoutLocked((ProcessRecord) r.app.mOwner);
}
public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
@@ -11683,35 +11144,8 @@
}
@Override
- public void setVrThread(int tid) {
- enforceSystemHasVrFeature();
- synchronized (this) {
- synchronized (mPidsSelfLocked) {
- final int pid = Binder.getCallingPid();
- final ProcessRecord proc = mPidsSelfLocked.get(pid);
- mVrController.setVrThreadLocked(tid, pid, proc);
- }
- }
- }
-
- @Override
public void setPersistentVrThread(int tid) {
- if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
- final String msg = "Permission Denial: setPersistentVrThread() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + permission.RESTRICTED_VR_ACCESS;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- enforceSystemHasVrFeature();
- synchronized (this) {
- synchronized (mPidsSelfLocked) {
- final int pid = Binder.getCallingPid();
- final ProcessRecord proc = mPidsSelfLocked.get(pid);
- mVrController.setPersistentVrThreadLocked(tid, pid, proc);
- }
- }
+ mActivityTaskManager.setPersistentVrThread(tid);
}
/**
@@ -11762,17 +11196,6 @@
return false;
}
- /**
- * Check that we have the features required for VR-related API calls, and throw an exception if
- * not.
- */
- void enforceSystemHasVrFeature() {
- if (!mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
- }
-
@Override
public void setRenderThread(int tid) {
synchronized (this) {
@@ -11795,7 +11218,7 @@
Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
}
// promote to FIFO now
- if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (proc.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
if (mUseFifoUiScheduling) {
setThreadScheduler(proc.renderThreadTid,
@@ -11827,7 +11250,7 @@
@Override
public boolean isVrModePackageEnabled(ComponentName packageName) {
- enforceSystemHasVrFeature();
+ mActivityTaskManager.enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -11839,13 +11262,6 @@
return mActivityTaskManager.isTopActivityImmersive();
}
- /**
- * @return whether the system should disable UI modes incompatible with VR mode.
- */
- boolean shouldDisableNonVrUiLocked() {
- return mVrController.shouldDisableNonVrUiLocked();
- }
-
@Override
public boolean isTopOfTask(IBinder token) {
return mActivityTaskManager.isTopOfTask(token);
@@ -12307,78 +11723,22 @@
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
- final boolean freeformWindowManagement =
- mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
- || Settings.Global.getInt(
- resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ mActivityTaskManager.retrieveSettings(resolver);
- final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
- final boolean supportsPictureInPicture = supportsMultiWindow &&
- mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- final boolean supportsSplitScreenMultiWindow =
- ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
- final boolean supportsMultiDisplay = mContext.getPackageManager()
- .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
final boolean alwaysFinishActivities =
Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
- final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
- final boolean forceResizable = Settings.Global.getInt(
- resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
final long waitForNetworkTimeoutMs = Settings.Global.getLong(resolver,
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
- final boolean supportsLeanbackOnly =
- mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
mHiddenApiBlacklist.registerObserver();
- // Transfer any global setting for forcing RTL layout, into a System Property
- SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
-
- final Configuration configuration = new Configuration();
- Settings.System.getConfiguration(resolver, configuration);
- if (forceRtl) {
- // This will take care of setting the correct layout direction flags
- configuration.setLayoutDirection(configuration.locale);
- }
-
synchronized (this) {
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
- mSupportsLeanbackOnly = supportsLeanbackOnly;
- mForceResizableActivities = forceResizable;
- final boolean multiWindowFormEnabled = freeformWindowManagement
- || supportsSplitScreenMultiWindow
- || supportsPictureInPicture
- || supportsMultiDisplay;
- if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
- mSupportsMultiWindow = true;
- mSupportsFreeformWindowManagement = freeformWindowManagement;
- mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
- mSupportsPictureInPicture = supportsPictureInPicture;
- mSupportsMultiDisplay = supportsMultiDisplay;
- } else {
- mSupportsMultiWindow = false;
- mSupportsFreeformWindowManagement = false;
- mSupportsSplitScreenMultiWindow = false;
- mSupportsPictureInPicture = false;
- mSupportsMultiDisplay = false;
- }
- mWindowManager.setForceResizableTasks(mForceResizableActivities);
- mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
- // This happens before any activities are started, so we can change global configuration
- // in-place.
- updateConfigurationLocked(configuration, null, true);
- final Configuration globalConfig = getGlobalConfiguration();
- if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
-
// Load resources only after the current configuration has been set.
final Resources res = mContext.getResources();
- mThumbnailWidth = res.getDimensionPixelSize(
- com.android.internal.R.dimen.thumbnail_width);
- mThumbnailHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.thumbnail_height);
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
mUserController.mUserSwitchUiEnabled = !res.getBoolean(
@@ -12386,14 +11746,6 @@
mUserController.mMaxRunningUsers = res.getInteger(
com.android.internal.R.integer.config_multiuserMaxRunningUsers);
- if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
- mFullscreenThumbnailScale = (float) res
- .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
- (float) globalConfig.screenWidthDp;
- } else {
- mFullscreenThumbnailScale = res.getFraction(
- com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
- }
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
}
}
@@ -12415,10 +11767,8 @@
mLocalDeviceIdleController
= LocalServices.getService(DeviceIdleController.LocalService.class);
mActivityTaskManager.onSystemReady();
- mVrController.onSystemReady();
// Make sure we have the current profile info, since it is needed for security checks.
mUserController.onSystemReady();
- mRecentTasks.onSystemReadyLocked();
mAppOpsService.systemReady();
mSystemReady = true;
}
@@ -12836,7 +12186,7 @@
final boolean isFatal = Build.IS_ENG || Settings.Global
.getInt(mContext.getContentResolver(), Settings.Global.WTF_IS_FATAL, 0) != 0;
- final boolean isSystem = (r == null) || r.persistent;
+ final boolean isSystem = (r == null) || r.isPersistent();
if (isFatal && !isSystem) {
mAppErrors.crashApplication(r, crashInfo);
@@ -12999,8 +12349,8 @@
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
}
- if (parent != null && parent.app != null && parent.app.pid != process.pid) {
- sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
+ if (parent != null && parent.app != null && parent.app.getPid() != process.pid) {
+ sb.append("Parent-Process: ").append(parent.app.mName).append("\n");
}
if (parent != null && parent != activity) {
sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
@@ -13102,25 +12452,27 @@
if (!allUsers && app.userId != userId) {
continue;
}
- if ((app.thread != null) && (app.crashing || app.notResponding)) {
+ final boolean crashing = app.isCrashing();
+ final boolean notResponding = app.isNotResponding();
+ if ((app.thread != null) && (crashing || notResponding)) {
// This one's in trouble, so we'll generate a report for it
// crashes are higher priority (in case there's a crash *and* an anr)
ActivityManager.ProcessErrorStateInfo report = null;
- if (app.crashing) {
+ if (crashing) {
report = app.crashingReport;
- } else if (app.notResponding) {
+ } else if (notResponding) {
report = app.notRespondingReport;
}
if (report != null) {
if (errList == null) {
- errList = new ArrayList<ActivityManager.ProcessErrorStateInfo>(1);
+ errList = new ArrayList<>(1);
}
errList.add(report);
} else {
Slog.w(TAG, "Missing app error report, app = " + app.processName +
- " crashing = " + app.crashing +
- " notResponding = " + app.notResponding);
+ " crashing = " + crashing +
+ " notResponding = " + notResponding);
}
}
}
@@ -13150,10 +12502,10 @@
if (mHeavyWeightProcess == app) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
}
- if (app.persistent) {
+ if (app.isPersistent()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
}
- if (app.activities.size() > 0) {
+ if (app.hasActivities()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
}
outInfo.lastTrimLevel = app.trimMemoryLevel;
@@ -13176,7 +12528,7 @@
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
callingUid) == PackageManager.PERMISSION_GRANTED;
final int userId = UserHandle.getUserId(callingUid);
- final boolean allUids = isGetTasksAllowed(
+ final boolean allUids = mAtmInternal.isGetTasksAllowed(
"getRunningAppProcesses", Binder.getCallingPid(), callingUid);
synchronized (this) {
@@ -13187,7 +12539,7 @@
|| (!allUids && app.uid != callingUid)) {
continue;
}
- if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
+ if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
@@ -13200,7 +12552,7 @@
app.adjSourceProcState);
} else if (app.adjSource instanceof ActivityRecord) {
ActivityRecord r = (ActivityRecord)app.adjSource;
- if (r.app != null) currApp.importanceReasonPid = r.app.pid;
+ if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
}
if (app.adjTarget instanceof ComponentName) {
currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
@@ -13443,8 +12795,8 @@
}
} else if ("recents".equals(cmd) || "r".equals(cmd)) {
synchronized (this) {
- if (mRecentTasks != null) {
- mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage);
+ if (mActivityTaskManager.getRecentTasks() != null) {
+ mActivityTaskManager.getRecentTasks().dump(pw, true /* dumpAll */, dumpPackage);
}
}
} else if ("binder-proxies".equals(cmd)) {
@@ -13647,8 +12999,8 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- if (mRecentTasks != null) {
- mRecentTasks.dump(pw, dumpAll, dumpPackage);
+ if (mActivityTaskManager.getRecentTasks() != null) {
+ mActivityTaskManager.getRecentTasks().dump(pw, dumpAll, dumpPackage);
}
pw.println();
if (dumpAll) {
@@ -13724,8 +13076,8 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- if (mRecentTasks != null) {
- mRecentTasks.dump(pw, dumpAll, dumpPackage);
+ if (mActivityTaskManager.getRecentTasks() != null) {
+ mActivityTaskManager.getRecentTasks().dump(pw, dumpAll, dumpPackage);
}
pw.println();
if (dumpAll) {
@@ -13790,7 +13142,7 @@
private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
- mActivityStartController.dump(pw, "", dumpPackage);
+ mActivityTaskManager.getActivityStartController().dump(pw, "", dumpPackage);
}
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -14003,11 +13355,11 @@
pw.println(" All known processes:");
needSep = true;
}
- pw.print(r.persistent ? " *PERS*" : " *APP*");
+ pw.print(r.isPersistent() ? " *PERS*" : " *APP*");
pw.print(" UID "); pw.print(procs.keyAt(ia));
pw.print(" "); pw.println(r);
r.dump(pw, " ");
- if (r.persistent) {
+ if (r.isPersistent()) {
numPers++;
}
}
@@ -14159,27 +13511,27 @@
needSep = false;
mUserController.dump(pw, dumpAll);
}
- if (mHomeProcess != null && (dumpPackage == null
- || mHomeProcess.pkgList.containsKey(dumpPackage))) {
+ if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
- pw.println(" mHomeProcess: " + mHomeProcess);
+ pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
}
- if (mPreviousProcess != null && (dumpPackage == null
- || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+ if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
- pw.println(" mPreviousProcess: " + mPreviousProcess);
+ pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
}
- if (dumpAll && (mPreviousProcess == null || dumpPackage == null
- || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+ if (dumpAll && (mActivityTaskManager.mPreviousProcess == null || dumpPackage == null
+ || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
StringBuilder sb = new StringBuilder(128);
sb.append(" mPreviousProcessVisibleTime: ");
- TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
+ TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
@@ -14204,7 +13556,7 @@
}
if (dumpAll) {
if (dumpPackage == null) {
- pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
+ pw.println(" mConfigWillChange: " + mActivityTaskManager.getFocusedStack().mConfigWillChange);
}
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
@@ -14287,7 +13639,7 @@
pw.println(" mRunningVoice=" + mRunningVoice);
pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
}
- pw.println(" mVrController=" + mVrController);
+ mActivityTaskManager.dumpVrControllerLocked(pw);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -14369,10 +13721,10 @@
pw.println(" mNativeDebuggingApp=" + mNativeDebuggingApp);
}
}
- if (mAllowAppSwitchUids.size() > 0) {
+ if (mActivityTaskManager.mAllowAppSwitchUids.size() > 0) {
boolean printed = false;
- for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
+ for (int i = 0; i < mActivityTaskManager.mAllowAppSwitchUids.size(); i++) {
+ ArrayMap<String, Integer> types = mActivityTaskManager.mAllowAppSwitchUids.valueAt(i);
for (int j = 0; j < types.size(); j++) {
if (dumpPackage == null ||
UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
@@ -14385,7 +13737,7 @@
printed = true;
}
pw.print(" User ");
- pw.print(mAllowAppSwitchUids.keyAt(i));
+ pw.print(mActivityTaskManager.mAllowAppSwitchUids.keyAt(i));
pw.print(": Type ");
pw.print(types.keyAt(j));
pw.print(" = ");
@@ -14399,9 +13751,9 @@
if (mAlwaysFinishActivities) {
pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities);
}
- if (mController != null) {
- pw.println(" mController=" + mController
- + " mControllerIsAMonkey=" + mControllerIsAMonkey);
+ if (mActivityTaskManager.mController != null) {
+ pw.println(" mController=" + mActivityTaskManager.mController
+ + " mControllerIsAMonkey=" + mActivityTaskManager.mControllerIsAMonkey);
}
if (dumpAll) {
pw.println(" Total persistent processes: " + numPers);
@@ -14475,7 +13827,7 @@
continue;
}
r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
- if (r.persistent) {
+ if (r.isPersistent()) {
numPers++;
}
}
@@ -14582,18 +13934,18 @@
if (dumpPackage == null) {
mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
- proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getFocusedStack().mConfigWillChange);
}
- if (mHomeProcess != null && (dumpPackage == null
- || mHomeProcess.pkgList.containsKey(dumpPackage))) {
- mHomeProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
+ if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
+ ((ProcessRecord) mActivityTaskManager.mHomeProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
}
- if (mPreviousProcess != null && (dumpPackage == null
- || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
- mPreviousProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
- proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+ if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ ((ProcessRecord) mActivityTaskManager.mPreviousProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
@@ -14655,7 +14007,8 @@
proto.end(vrToken);
}
- mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+ mActivityTaskManager.writeVrControllerToProto(
+ proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -14731,10 +14084,10 @@
if (dumpPackage == null) {
proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
- if (mController != null) {
+ if (mActivityTaskManager.mController != null) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
- proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mController.toString());
- proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mActivityTaskManager.mController.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mActivityTaskManager.mControllerIsAMonkey);
proto.end(token);
}
proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
@@ -14863,8 +14216,8 @@
dumpProcessesToGc(pw, needSep, null);
pw.println();
- pw.println(" mHomeProcess: " + mHomeProcess);
- pw.println(" mPreviousProcess: " + mPreviousProcess);
+ pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
+ pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
if (mHeavyWeightProcess != null) {
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
@@ -15036,20 +14389,20 @@
pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
pw.print(" pid=");
- if (r.app != null) pw.println(r.app.pid);
+ if (r.hasProcess()) pw.println(r.app.getPid());
else pw.println("(not running)");
if (dumpAll) {
r.dump(pw, innerPrefix);
}
}
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
// flush anything that is already in the PrintWriter since the thread is going
// to write to the file descriptor directly
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpActivity(tp.getWriteFd(),
+ r.app.getThread().dumpActivity(tp.getWriteFd(),
r.appToken, innerPrefix, args);
tp.go(fd);
} finally {
@@ -15404,9 +14757,9 @@
continue;
}
pw.println(String.format("%s%s #%2d: %s",
- prefix, (r.persistent ? persistentLabel : normalLabel),
+ prefix, (r.isPersistent() ? persistentLabel : normalLabel),
i, r.toString()));
- if (r.persistent) {
+ if (r.isPersistent()) {
numPers++;
}
}
@@ -15459,7 +14812,7 @@
ProcessRecord r = list.get(i).first;
long token = proto.start(fieldId);
String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
- proto.write(ProcessOomProto.PERSISTENT, r.persistent);
+ proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
@@ -15482,7 +14835,7 @@
}
if (r.foregroundActivities) {
proto.write(ProcessOomProto.ACTIVITIES, true);
- } else if (r.foregroundServices) {
+ } else if (r.hasForegroundServices()) {
proto.write(ProcessOomProto.SERVICES, true);
}
proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
@@ -15579,14 +14932,14 @@
char foreground;
if (r.foregroundActivities) {
foreground = 'A';
- } else if (r.foregroundServices) {
+ } else if (r.hasForegroundServices()) {
foreground = 'S';
} else {
foreground = ' ';
}
String procState = ProcessList.makeProcStateString(r.curProcState);
pw.print(prefix);
- pw.print(r.persistent ? persistentLabel : normalLabel);
+ pw.print(r.isPersistent() ? persistentLabel : normalLabel);
pw.print(" #");
int num = (origList.size()-1)-list.get(i).second;
if (num < 10) pw.print(' ');
@@ -16201,7 +15554,7 @@
thread = r.thread;
pid = r.pid;
oomAdj = r.getSetAdjWithServices();
- hasActivities = r.activities.size() > 0;
+ hasActivities = r.hasActivities();
}
if (thread != null) {
if (!opts.isCheckinRequest && opts.dumpDetails) {
@@ -16268,7 +15621,7 @@
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
- reportType, endTime-startTime, r.pkgList);
+ reportType, endTime-startTime, r.pkgList.mPkgList);
}
}
@@ -16704,7 +16057,7 @@
thread = r.thread;
pid = r.pid;
oomAdj = r.getSetAdjWithServices();
- hasActivities = r.activities.size() > 0;
+ hasActivities = r.hasActivities();
}
if (thread == null) {
continue;
@@ -16766,7 +16119,7 @@
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
- reportType, endTime-startTime, r.pkgList);
+ reportType, endTime-startTime, r.pkgList.mPkgList);
}
}
@@ -17332,7 +16685,7 @@
ProcessRecord capp = conn.client;
conn.dead = true;
if (conn.stableCount > 0) {
- if (!capp.persistent && capp.thread != null
+ if (!capp.isPersistent() && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
capp.kill("depends on provider "
@@ -17394,8 +16747,8 @@
app.waitDialog = null;
}
- app.crashing = false;
- app.notResponding = false;
+ app.setCrashing(false);
+ app.setNotResponding(false);
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
@@ -17504,7 +16857,7 @@
return false;
}
- if (!app.persistent || app.isolated) {
+ if (!app.isPersistent() || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
if (!replacingPid) {
@@ -17528,12 +16881,7 @@
TAG_CLEANUP, "Clean-up removing on hold: " + app);
mProcessesOnHold.remove(app);
- if (app == mHomeProcess) {
- mHomeProcess = null;
- }
- if (app == mPreviousProcess) {
- mPreviousProcess = null;
- }
+ mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
if (restart && !app.isolated) {
// We have components that still need to be running in the
@@ -17601,8 +16949,8 @@
final int callingUid = Binder.getCallingUid();
final boolean canInteractAcrossUsers = (ActivityManager.checkUidPermission(
INTERACT_ACROSS_USERS_FULL, callingUid) == PERMISSION_GRANTED);
- final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(),
- callingUid);
+ final boolean allowed = mAtmInternal.isGetTasksAllowed("getServices",
+ Binder.getCallingPid(), callingUid);
synchronized (this) {
return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid,
allowed, canInteractAcrossUsers);
@@ -17977,6 +17325,7 @@
// done with this agent
public void unbindBackupAgent(ApplicationInfo appInfo) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo);
+ enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent");
if (appInfo == null) {
Slog.w(TAG, "unbind backup agent for null app");
return;
@@ -18577,7 +17926,7 @@
isCallerSystem = true;
break;
default:
- isCallerSystem = (callerApp != null) && callerApp.persistent;
+ isCallerSystem = (callerApp != null) && callerApp.isPersistent();
break;
}
@@ -18670,14 +18019,16 @@
forceStopPackageLocked(list[i], -1, false, true, true,
false, false, userId, "storage unmount");
}
- mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
+ mActivityTaskManager.getRecentTasks().cleanupLocked(
+ UserHandle.USER_ALL);
sendPackageBroadcastLocked(
ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,
list, userId);
}
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
- mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
+ mActivityTaskManager.getRecentTasks().cleanupLocked(
+ UserHandle.USER_ALL);
break;
case Intent.ACTION_PACKAGE_REMOVED:
case Intent.ACTION_PACKAGE_CHANGED:
@@ -18710,7 +18061,7 @@
removeUriPermissionsForPackageLocked(ssp, userId, true,
false);
- mRecentTasks.removeTasksByPackageName(ssp, userId);
+ mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId);
mServices.forceStopPackageLocked(ssp, userId);
mAppWarnings.onPackageUninstalled(ssp);
@@ -18740,7 +18091,7 @@
Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
synchronized(ActivityManagerService.this) {
- mRecentTasks.onPackagesSuspendedChanged(
+ mActivityTaskManager.getRecentTasks().onPackagesSuspendedChanged(
packageNames, suspended, userHandle);
}
break;
@@ -19571,10 +18922,6 @@
return config;
}
- ActivityStack getFocusedStack() {
- return mStackSupervisor.getFocusedStack();
- }
-
@Override
public StackInfo getFocusedStackInfo() throws RemoteException {
return mActivityTaskManager.getFocusedStackInfo();
@@ -19604,18 +18951,7 @@
int userId = UserHandle.getCallingUserId();
- synchronized(this) {
- updatePersistentConfigurationLocked(values, userId);
- }
- }
-
- private void updatePersistentConfigurationLocked(Configuration values, @UserIdInt int userId) {
- final long origId = Binder.clearCallingIdentity();
- try {
- updateConfigurationLocked(values, null, false, true, userId, false /* deferResume */);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mActivityTaskManager.updatePersistentConfiguration(values, userId);
}
private void updateFontScaleIfNeeded(@UserIdInt int userId) {
@@ -19630,7 +18966,7 @@
final Configuration configuration
= mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
configuration.fontScale = scaleFactor;
- updatePersistentConfigurationLocked(configuration, userId);
+ mActivityTaskManager.updatePersistentConfiguration(configuration, userId);
}
}
@@ -19658,356 +18994,6 @@
return mActivityTaskManager.updateConfiguration(values);
}
- void updateUserConfigurationLocked() {
- final Configuration configuration = new Configuration(getGlobalConfiguration());
- final int currentUserId = mUserController.getCurrentUserId();
- Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
- currentUserId, Settings.System.canWrite(mContext));
- updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
- false /* persistent */, currentUserId, false /* deferResume */);
- }
-
- boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale) {
- return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
- }
-
- boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean deferResume) {
- // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
- return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
- UserHandle.USER_NULL, deferResume);
- }
-
- // To cache the list of supported system locales
- private String[] mSupportedSystemLocales = null;
-
- private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume) {
- return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
- deferResume, null /* result */);
- }
-
- /**
- * Do either or both things: (1) change the current configuration, and (2)
- * make sure the given activity is running with the (now) current
- * configuration. Returns true if the activity has been left running, or
- * false if <var>starting</var> is being destroyed to match the new
- * configuration.
- *
- * @param userId is only used when persistent parameter is set to true to persist configuration
- * for that particular user
- */
- boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume,
- ActivityTaskManagerService.UpdateConfigurationResult result) {
- int changes = 0;
- boolean kept = true;
-
- if (mWindowManager != null) {
- mWindowManager.deferSurfaceLayout();
- }
- try {
- if (values != null) {
- changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
- deferResume);
- }
-
- kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
- } finally {
- if (mWindowManager != null) {
- mWindowManager.continueSurfaceLayout();
- }
- }
-
- if (result != null) {
- result.changes = changes;
- result.activityRelaunched = !kept;
- }
- return kept;
- }
-
- /**
- * Returns true if this configuration change is interesting enough to send an
- * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
- */
- private static boolean isSplitConfigurationChange(int configDiff) {
- return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
- }
-
- /** Update default (global) configuration and notify listeners about changes. */
- private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
- boolean persistent, int userId, boolean deferResume) {
- mTempConfig.setTo(getGlobalConfiguration());
- final int changes = mTempConfig.updateFrom(values);
- if (changes == 0) {
- // Since calling to Activity.setRequestedOrientation leads to freezing the window with
- // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
- // performDisplayOverrideConfigUpdate in order to send the new display configuration
- // (even if there are no actual changes) to unfreeze the window.
- performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
- return 0;
- }
-
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
- "Updating global configuration to: " + values);
-
- EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
- StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
- values.colorMode,
- values.densityDpi,
- values.fontScale,
- values.hardKeyboardHidden,
- values.keyboard,
- values.keyboardHidden,
- values.mcc,
- values.mnc,
- values.navigation,
- values.navigationHidden,
- values.orientation,
- values.screenHeightDp,
- values.screenLayout,
- values.screenWidthDp,
- values.smallestScreenWidthDp,
- values.touchscreen,
- values.uiMode);
-
-
- if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
- final LocaleList locales = values.getLocales();
- int bestLocaleIndex = 0;
- if (locales.size() > 1) {
- if (mSupportedSystemLocales == null) {
- mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
- }
- bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
- }
- SystemProperties.set("persist.sys.locale",
- locales.get(bestLocaleIndex).toLanguageTag());
- LocaleList.setDefault(locales, bestLocaleIndex);
- mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
- locales.get(bestLocaleIndex)));
- }
-
- mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
- mTempConfig.seq = mConfigurationSeq;
-
- // Update stored global config and notify everyone about the change.
- mStackSupervisor.onConfigurationChanged(mTempConfig);
-
- Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
- // TODO(multi-display): Update UsageEvents#Event to include displayId.
- mUsageStatsService.reportConfigurationChange(mTempConfig,
- mUserController.getCurrentUserId());
-
- // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
- updateShouldShowDialogsLocked(mTempConfig);
-
- AttributeCache ac = AttributeCache.instance();
- if (ac != null) {
- ac.updateConfiguration(mTempConfig);
- }
-
- // Make sure all resources in our process are updated right now, so that anyone who is going
- // to retrieve resource values after we return will be sure to get the new ones. This is
- // especially important during boot, where the first config change needs to guarantee all
- // resources have that config before following boot code is executed.
- mSystemThread.applyConfigurationToResources(mTempConfig);
-
- // We need another copy of global config because we're scheduling some calls instead of
- // running them in place. We need to be sure that object we send will be handled unchanged.
- final Configuration configCopy = new Configuration(mTempConfig);
- if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
- Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
- msg.obj = configCopy;
- msg.arg1 = userId;
- mHandler.sendMessage(msg);
- }
-
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
- try {
- if (app.thread != null) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
- + app.processName + " new config " + configCopy);
- mLifecycleManager.scheduleTransaction(app.thread,
- ConfigurationChangeItem.obtain(configCopy));
- }
- } catch (Exception e) {
- Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
- }
- }
-
- Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- UserHandle.USER_ALL);
- if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
- intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- if (initLocale || !mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- }
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- UserHandle.USER_ALL);
- }
-
- // Send a broadcast to PackageInstallers if the configuration change is interesting
- // for the purposes of installing additional splits.
- if (!initLocale && isSplitConfigurationChange(changes)) {
- intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-
- // Typically only app stores will have this permission.
- String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- }
-
- // Override configuration of the default display duplicates global config, so we need to
- // update it also. This will also notify WindowManager about changes.
- performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
- DEFAULT_DISPLAY);
-
- return changes;
- }
-
- boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean deferResume, int displayId) {
- return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
- displayId, null /* result */);
- }
-
- /**
- * Updates override configuration specific for the selected display. If no config is provided,
- * new one will be computed in WM based on current display info.
- */
- boolean updateDisplayOverrideConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean deferResume, int displayId,
- ActivityTaskManagerService.UpdateConfigurationResult result) {
- int changes = 0;
- boolean kept = true;
-
- if (mWindowManager != null) {
- mWindowManager.deferSurfaceLayout();
- }
- try {
- if (values != null) {
- if (displayId == DEFAULT_DISPLAY) {
- // Override configuration of the default display duplicates global config, so
- // we're calling global config update instead for default display. It will also
- // apply the correct override config.
- changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
- false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
- } else {
- changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
- }
- }
-
- kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
- } finally {
- if (mWindowManager != null) {
- mWindowManager.continueSurfaceLayout();
- }
- }
-
- if (result != null) {
- result.changes = changes;
- result.activityRelaunched = !kept;
- }
- return kept;
- }
-
- private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
- int displayId) {
- mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
- final int changes = mTempConfig.updateFrom(values);
- if (changes != 0) {
- Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
- + mTempConfig + " for displayId=" + displayId);
- mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
-
- final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
- if (isDensityChange && displayId == DEFAULT_DISPLAY) {
- mAppWarnings.onDensityChanged();
-
- killAllBackgroundProcessesExcept(N,
- ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- }
- }
-
- // Update the configuration with WM first and check if any of the stacks need to be resized
- // due to the configuration change. If so, resize the stacks now and do any relaunches if
- // necessary. This way we don't need to relaunch again afterwards in
- // ensureActivityConfiguration().
- if (mWindowManager != null) {
- final int[] resizedStacks =
- mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
- if (resizedStacks != null) {
- for (int stackId : resizedStacks) {
- resizeStackWithBoundsFromWindowManager(stackId, deferResume);
- }
- }
- }
-
- return changes;
- }
-
- /** Applies latest configuration and/or visibility updates if needed. */
- private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
- boolean kept = true;
- final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
- // mainStack is null during startup.
- if (mainStack != null) {
- if (changes != 0 && starting == null) {
- // If the configuration changed, and the caller is not already
- // in the process of starting an activity, then find the top
- // activity to check if its configuration needs to change.
- starting = mainStack.topRunningActivityLocked();
- }
-
- if (starting != null) {
- kept = starting.ensureActivityConfiguration(changes,
- false /* preserveWindow */);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
- !PRESERVE_WINDOWS);
- }
- }
-
- return kept;
- }
-
- /** Helper method that requests bounds from WM and applies them to stack. */
- private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
- final Rect newStackBounds = new Rect();
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
-
- // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
- if (stack == null) {
- final StringWriter writer = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(writer);
- mStackSupervisor.dumpDisplays(printWriter);
- printWriter.flush();
-
- Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
- }
-
- stack.getBoundsForNewConfiguration(newStackBounds);
- mStackSupervisor.resizeStackLocked(
- stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
- }
-
/**
* Decide based on the configuration whether we should show the ANR,
* crash, etc dialogs. The idea is that if there is no affordance to
@@ -20017,7 +19003,7 @@
* A thought: SystemUI might also want to get told about this, the Power
* dialog / global actions also might want different behaviors.
*/
- private void updateShouldShowDialogsLocked(Configuration config) {
+ void updateShouldShowDialogsLocked(Configuration config) {
final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
&& config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
&& config.navigation == Configuration.NAVIGATION_NONAV);
@@ -20182,7 +19168,7 @@
if (app.thread == null) {
app.adjSeq = mAdjSeq;
- app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ;
app.completedAdjSeq = app.adjSeq;
@@ -20195,7 +19181,7 @@
app.empty = false;
app.cached = false;
- final int activitiesSize = app.activities.size();
+ final WindowProcessController wpc = app.getWindowProcessController();
final int appUid = app.info.uid;
final int logUid = mCurOomAdjUid;
@@ -20211,7 +19197,7 @@
app.adjSeq = mAdjSeq;
app.curRawAdj = app.maxAdj;
app.foregroundActivities = false;
- app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
@@ -20220,29 +19206,24 @@
app.systemNoUi = true;
if (app == TOP_APP) {
app.systemNoUi = false;
- app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
app.adjType = "pers-top-activity";
} else if (app.hasTopUi) {
// sched group/proc state adjustment is below
app.systemNoUi = false;
app.adjType = "pers-top-ui";
- } else if (activitiesSize > 0) {
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = app.activities.get(j);
- if (r.visible) {
- app.systemNoUi = false;
- }
- }
+ } else if (wpc.hasVisibleActivities()) {
+ app.systemNoUi = false;
}
if (!app.systemNoUi) {
if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// screen on, promote UI
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
- app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
} else {
// screen off, restrict UI scheduling
app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
}
}
app.curAdj = app.maxAdj;
@@ -20341,120 +19322,127 @@
}
// Examine all activities if not already foreground.
- if (!foregroundActivities && activitiesSize > 0) {
- int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = app.activities.get(j);
- if (r.app != app) {
- Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
- + " instead of expected " + app);
- if (r.app == null || (r.app.uid == app.uid)) {
- // Only fix things up when they look sane
- r.setProcess(app);
- } else {
- continue;
- }
- }
- if (r.visible) {
- // App has a visible activity; only upgrade adjustment.
- if (adj > ProcessList.VISIBLE_APP_ADJ) {
- adj = ProcessList.VISIBLE_APP_ADJ;
- app.adjType = "vis-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise adj to vis-activity: " + app);
+ if (!foregroundActivities && wpc.hasActivities()) {
+ final int[] adjHolder = new int[1];
+ adjHolder[0] = adj;
+ final boolean[] foregroundActivitiesHolder = new boolean[1];
+ foregroundActivitiesHolder[0] = foregroundActivities;
+ int[] procStateHolder = new int[1];
+ procStateHolder[0] = procState;
+ int[] schedGroupHolder = new int[1];
+ schedGroupHolder[0] = schedGroup;
+
+ int minLayer = wpc.computeOomAdjFromActivities(ProcessList.VISIBLE_APP_LAYER_MAX,
+ new WindowProcessController.ComputeOomAdjCallback() {
+ @Override
+ public void onVisibleActivity() {
+ // App has a visible activity; only upgrade adjustment.
+ if (adjHolder[0] > ProcessList.VISIBLE_APP_ADJ) {
+ adjHolder[0] = ProcessList.VISIBLE_APP_ADJ;
+ app.adjType = "vis-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise adj to vis-activity: " + app);
+ }
+ }
+ if (procStateHolder[0] > PROCESS_STATE_CUR_TOP) {
+ procStateHolder[0] = PROCESS_STATE_CUR_TOP;
+ app.adjType = "vis-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to vis-activity (top): " + app);
+ }
+ }
+ if (schedGroupHolder[0] < ProcessList.SCHED_GROUP_DEFAULT) {
+ schedGroupHolder[0] = ProcessList.SCHED_GROUP_DEFAULT;
+ }
+ app.cached = false;
+ app.empty = false;
+ foregroundActivitiesHolder[0] = true;
}
- }
- if (procState > PROCESS_STATE_CUR_TOP) {
- procState = PROCESS_STATE_CUR_TOP;
- app.adjType = "vis-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to vis-activity (top): " + app);
+
+ @Override
+ public void onPausedActivity() {
+ if (adjHolder[0] > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adjHolder[0] = ProcessList.PERCEPTIBLE_APP_ADJ;
+ app.adjType = "pause-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise adj to pause-activity: " + app);
+ }
+ }
+ if (procStateHolder[0] > PROCESS_STATE_CUR_TOP) {
+ procStateHolder[0] = PROCESS_STATE_CUR_TOP;
+ app.adjType = "pause-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to pause-activity (top): " + app);
+ }
+ }
+ if (schedGroupHolder[0] < ProcessList.SCHED_GROUP_DEFAULT) {
+ schedGroupHolder[0] = ProcessList.SCHED_GROUP_DEFAULT;
+ }
+ app.cached = false;
+ app.empty = false;
+ foregroundActivitiesHolder[0] = true;
}
- }
- if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- }
- app.cached = false;
- app.empty = false;
- foregroundActivities = true;
- final TaskRecord task = r.getTask();
- if (task != null && minLayer > 0) {
- final int layer = task.mLayerRank;
- if (layer >= 0 && minLayer > layer) {
- minLayer = layer;
+
+ @Override
+ public void onStoppingActivity(boolean finishing) {
+ if (adjHolder[0] > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adjHolder[0] = ProcessList.PERCEPTIBLE_APP_ADJ;
+ app.adjType = "stop-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise adj to stop-activity: " + app);
+ }
+ }
+ // For the process state, we will at this point consider the process to
+ // be cached. It will be cached either as an activity or empty depending
+ // on whether the activity is finishing. We do this so that we can treat
+ // the process as cached for purposes of memory trimming (determining
+ // current memory level, trim command to send to process) since there
+ // can be an arbitrary number of stopping processes and they should soon
+ // all go into the cached state.
+ if (!finishing) {
+ if (procStateHolder[0] > PROCESS_STATE_LAST_ACTIVITY) {
+ procStateHolder[0] = PROCESS_STATE_LAST_ACTIVITY;
+ app.adjType = "stop-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to stop-activity: " + app);
+ }
+ }
+ }
+ app.cached = false;
+ app.empty = false;
+ foregroundActivitiesHolder[0] = true;
}
- }
- break;
- } else if (r.isState(ActivityState.PAUSING, ActivityState.PAUSED)) {
- if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.adjType = "pause-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise adj to pause-activity: " + app);
- }
- }
- if (procState > PROCESS_STATE_CUR_TOP) {
- procState = PROCESS_STATE_CUR_TOP;
- app.adjType = "pause-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to pause-activity (top): " + app);
- }
- }
- if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- }
- app.cached = false;
- app.empty = false;
- foregroundActivities = true;
- } else if (r.isState(ActivityState.STOPPING)) {
- if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.adjType = "stop-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise adj to stop-activity: " + app);
- }
- }
- // For the process state, we will at this point consider the
- // process to be cached. It will be cached either as an activity
- // or empty depending on whether the activity is finishing. We do
- // this so that we can treat the process as cached for purposes of
- // memory trimming (determing current memory level, trim command to
- // send to process) since there can be an arbitrary number of stopping
- // processes and they should soon all go into the cached state.
- if (!r.finishing) {
- if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
- app.adjType = "stop-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to stop-activity: " + app);
+
+ @Override
+ public void onOtherActivity() {
+ if (procStateHolder[0] > PROCESS_STATE_CACHED_ACTIVITY) {
+ procStateHolder[0] = PROCESS_STATE_CACHED_ACTIVITY;
+ app.adjType = "cch-act";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to cached activity: " + app);
+ }
}
}
- }
- app.cached = false;
- app.empty = false;
- foregroundActivities = true;
- } else {
- if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
- app.adjType = "cch-act";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to cached activity: " + app);
- }
- }
- }
- }
+ });
+
+ adj = adjHolder[0];
+ foregroundActivities = foregroundActivitiesHolder[0];
+ procState = procStateHolder[0];
+ schedGroup = schedGroupHolder[0];
+
if (adj == ProcessList.VISIBLE_APP_ADJ) {
adj += minLayer;
}
}
- if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.recentTasks.size() > 0) {
+
+ if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
app.adjType = "cch-rec";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -20464,7 +19452,7 @@
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- if (app.foregroundServices) {
+ if (app.hasForegroundServices()) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -20525,7 +19513,7 @@
}
}
- if (app == mHomeProcess) {
+ if (wpc == mActivityTaskManager.mHomeProcess) {
if (adj > ProcessList.HOME_APP_ADJ) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
@@ -20546,7 +19534,7 @@
}
}
- if (app == mPreviousProcess && app.activities.size() > 0) {
+ if (wpc == mActivityTaskManager.mPreviousProcess && app.hasActivities()) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
// This was the previous process that showed UI to the user.
// We want to try to keep it around more aggressively, to give
@@ -20559,8 +19547,8 @@
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
}
}
- if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "previous";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
@@ -20623,7 +19611,7 @@
"Raise procstate to started service: " + app);
}
}
- if (app.hasShownUi && app != mHomeProcess) {
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -20686,7 +19674,7 @@
}
int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
- if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
// doesn't propagate except under certain conditions.
@@ -20696,7 +19684,7 @@
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
// this guy more like a started service.
- if (app.hasShownUi && app != mHomeProcess) {
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -20729,7 +19717,7 @@
// about letting this process get into the LRU
// list to be killed and restarted if needed for
// memory.
- if (app.hasShownUi && app != mHomeProcess
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
adjType = "cch-bound-ui-services";
@@ -20773,9 +19761,10 @@
// This will treat important bound services identically to
// the top app, which may behave differently than generic
// foreground work.
- if (client.curSchedGroup > schedGroup) {
+ final int curSchedGroup = client.getCurrentSchedulingGroup();
+ if (curSchedGroup > schedGroup) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
- schedGroup = client.curSchedGroup;
+ schedGroup = curSchedGroup;
} else {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
@@ -20913,14 +19902,14 @@
}
int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
- if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
}
String adjType = null;
if (adj > clientAdj) {
- if (app.hasShownUi && app != mHomeProcess
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adjType = "cch-ui-provider";
} else {
@@ -20961,7 +19950,7 @@
if (procState > clientProcState) {
procState = clientProcState;
}
- if (client.curSchedGroup > schedGroup) {
+ if (client.getCurrentSchedulingGroup() > schedGroup) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
if (adjType != null) {
@@ -21014,8 +20003,8 @@
"Raise adj to recent provider: " + app);
}
}
- if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "recent-provider";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
@@ -21077,7 +20066,7 @@
} else if (app.treatLikeActivity) {
// This is a cached process, but somebody wants us to treat it like it has
// an activity, okay!
- procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+ procState = PROCESS_STATE_CACHED_ACTIVITY;
app.adjType = "cch-as-act";
}
}
@@ -21136,7 +20125,7 @@
// worry about this for max adj above, since max adj will always be used to
// keep it out of the cached vaues.
app.curAdj = app.modifyRawOomAdj(adj);
- app.curSchedGroup = schedGroup;
+ app.setCurrentSchedulingGroup(schedGroup);
app.curProcState = procState;
app.foregroundActivities = foregroundActivities;
app.completedAdjSeq = mAdjSeq;
@@ -21153,7 +20142,8 @@
EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
swapPss * 1024, rss * 1024, statType, procState, pssDuration);
proc.lastPssTime = now;
- proc.baseProcessTracker.addPss(pss, uss, rss, true, statType, pssDuration, proc.pkgList);
+ proc.baseProcessTracker.addPss(
+ pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
if (DEBUG_PSS) Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ " state=" + ProcessList.makeProcStateString(procState));
@@ -21504,7 +20494,7 @@
}
app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
+ " dur=" + checkDur + " limit=" + cpuLimit, true);
- app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
+ app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
}
}
app.lastCpuTime = app.curCpuTime;
@@ -21533,12 +20523,13 @@
app.verifiedAdj = ProcessList.INVALID_ADJ;
}
- if (app.setSchedGroup != app.curSchedGroup) {
+ final int curSchedGroup = app.getCurrentSchedulingGroup();
+ if (app.setSchedGroup != curSchedGroup) {
int oldSchedGroup = app.setSchedGroup;
- app.setSchedGroup = app.curSchedGroup;
+ app.setSchedGroup = curSchedGroup;
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
String msg = "Setting sched group of " + app.processName
- + " to " + app.curSchedGroup + ": " + app.adjType;
+ + " to " + curSchedGroup + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
if (app.waitingToKill != null && app.curReceivers.isEmpty()
@@ -21547,7 +20538,7 @@
success = false;
} else {
int processGroup;
- switch (app.curSchedGroup) {
+ switch (curSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
break;
@@ -21565,10 +20556,11 @@
long oldId = Binder.clearCallingIdentity();
try {
setProcessGroup(app.pid, processGroup);
- if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- mVrController.onTopProcChangedLocked(app);
+ mActivityTaskManager.onTopProcChangedLocked(
+ app.getWindowProcessController());
if (mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
app.savedPriority = Process.getThreadPriority(app.pid);
@@ -21599,8 +20591,9 @@
}
}
} else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
- app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- mVrController.onTopProcChangedLocked(app);
+ curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ mActivityTaskManager.onTopProcChangedLocked(
+ app.getWindowProcessController());
if (mUseFifoUiScheduling) {
try {
// Reset UI pipeline to SCHED_OTHER
@@ -21629,7 +20622,7 @@
} catch (Exception e) {
if (false) {
Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.curSchedGroup);
+ + " to " + app.getCurrentSchedulingGroup());
Slog.w(TAG, "at location", e);
}
} finally {
@@ -21641,16 +20634,16 @@
app.repForegroundActivities = app.foregroundActivities;
changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
}
- if (app.repProcState != app.curProcState) {
- app.repProcState = app.curProcState;
+ if (app.getReportedProcState() != app.curProcState) {
+ app.setReportedProcState(app.curProcState);
if (app.thread != null) {
try {
if (false) {
//RuntimeException h = new RuntimeException("here");
- Slog.i(TAG, "Sending new process state " + app.repProcState
+ Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+ " to " + app /*, h*/);
}
- app.thread.setProcessState(app.repProcState);
+ app.thread.setProcessState(app.getReportedProcState());
} catch (RemoteException e) {
}
}
@@ -21937,10 +20930,9 @@
}
private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
- if (proc.thread != null) {
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
- }
+ if (proc.thread != null && proc.baseProcessTracker != null) {
+ proc.baseProcessTracker.setState(
+ proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
}
}
@@ -21958,8 +20950,8 @@
@GuardedBy("this")
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
boolean oomAdj) {
- if (isForeground != proc.foregroundServices) {
- proc.foregroundServices = isForeground;
+ if (isForeground != proc.hasForegroundServices()) {
+ proc.setHasForegroundServices(isForeground);
ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
proc.info.uid);
if (isForeground) {
@@ -21991,7 +20983,8 @@
}
private final ActivityRecord resumedAppLocked() {
- ActivityRecord act = mStackSupervisor.getResumedActivityLocked();
+ final ActivityRecord act =
+ mStackSupervisor != null ? mStackSupervisor.getResumedActivityLocked() : null;
String pkg;
int uid;
if (act != null) {
@@ -22028,7 +21021,8 @@
@GuardedBy("this")
final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
final ActivityRecord TOP_ACT = resumedAppLocked();
- final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
+ final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
+ ? (ProcessRecord) TOP_ACT.app.mOwner : null;
final boolean wasCached = app.cached;
mAdjSeq++;
@@ -22053,7 +21047,8 @@
@GuardedBy("this")
final void updateOomAdjLocked() {
final ActivityRecord TOP_ACT = resumedAppLocked();
- final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
+ final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
+ ? (ProcessRecord) TOP_ACT.app.mOwner : null;
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
@@ -22073,7 +21068,9 @@
uidRec.reset();
}
- mStackSupervisor.rankTaskLayersIfNeeded();
+ if (mStackSupervisor != null) {
+ mStackSupervisor.rankTaskLayersIfNeeded();
+ }
mAdjSeq++;
mNewNumServiceProcs = 0;
@@ -22138,7 +21135,7 @@
// to the process, do that now.
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
switch (app.curProcState) {
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// This process is a cached process holding activities...
@@ -22224,7 +21221,7 @@
// Count the number of process types.
switch (app.curProcState) {
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
@@ -22267,7 +21264,7 @@
if (uidRec.curProcState > app.curProcState) {
uidRec.curProcState = app.curProcState;
}
- if (app.foregroundServices) {
+ if (app.hasForegroundServices()) {
uidRec.foregroundServices = true;
}
}
@@ -22342,8 +21339,8 @@
}
int factor = numTrimming/3;
int minFactor = 2;
- if (mHomeProcess != null) minFactor++;
- if (mPreviousProcess != null) minFactor++;
+ if (mActivityTaskManager.mHomeProcess != null) minFactor++;
+ if (mActivityTaskManager.mPreviousProcess != null) minFactor++;
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (int i=N-1; i>=0; i--) {
@@ -22361,19 +21358,6 @@
app.thread.scheduleTrimMemory(curLevel);
} catch (RemoteException e) {
}
- if (false) {
- // For now we won't do this; our memory trimming seems
- // to be good enough at this point that destroying
- // activities causes more harm than good.
- if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
- && app != mHomeProcess && app != mPreviousProcess) {
- // Need to do this on its own message because the stack may not
- // be in a consistent state at this point.
- // For these apps we will also finish their activities
- // to help them free memory.
- mStackSupervisor.scheduleDestroyAllActivities(app, "trim");
- }
- }
}
app.trimMemoryLevel = curLevel;
step++;
@@ -22924,7 +21908,7 @@
// has been removed.
for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
final ProcessRecord app = mRemovedProcesses.get(i);
- if (app.activities.size() == 0 && app.recentTasks.size() == 0
+ if (!app.hasActivitiesOrRecentTasks()
&& app.curReceivers.isEmpty() && app.services.size() == 0) {
Slog.i(
TAG, "Exiting empty application process "
@@ -22943,7 +21927,7 @@
cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
mRemovedProcesses.remove(i);
- if (app.persistent) {
+ if (app.isPersistent()) {
addAppLocked(app.info, null, false, null /* ABI override */);
}
}
@@ -22969,7 +21953,7 @@
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null && r.persistent) {
+ if (r.thread != null && r.isPersistent()) {
sendSignal(r.pid, sig);
}
}
@@ -23465,74 +22449,6 @@
}
@Override
- public SleepToken acquireSleepToken(String tag, int displayId) {
- Preconditions.checkNotNull(tag);
- return ActivityManagerService.this.acquireSleepToken(tag, displayId);
- }
-
- @Override
- public ComponentName getHomeActivityForUser(int userId) {
- synchronized (ActivityManagerService.this) {
- ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
- return homeActivity == null ? null : homeActivity.realActivity;
- }
- }
-
- @Override
- public void onUserRemoved(int userId) {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.onUserStoppedLocked(userId);
- }
- mBatteryStatsService.onUserRemoved(userId);
- mUserController.onUserRemoved(userId);
- }
-
- @Override
- public void onLocalVoiceInteractionStarted(IBinder activity,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.onLocalVoiceInteractionStartedLocked(activity,
- voiceSession, voiceInteractor);
- }
- }
-
- @Override
- public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
- synchronized (ActivityManagerService.this) {
- mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
- reasons, timestamp);
- }
- }
-
- @Override
- public void notifyAppTransitionFinished() {
- synchronized (ActivityManagerService.this) {
- mStackSupervisor.notifyAppTransitionDone();
- }
- }
-
- @Override
- public void notifyAppTransitionCancelled() {
- synchronized (ActivityManagerService.this) {
- mStackSupervisor.notifyAppTransitionDone();
- }
- }
-
- @Override
- public List<IBinder> getTopVisibleActivities() {
- synchronized (ActivityManagerService.this) {
- return mStackSupervisor.getTopVisibleActivities();
- }
- }
-
- @Override
- public void notifyDockedStackMinimizedChanged(boolean minimized) {
- synchronized (ActivityManagerService.this) {
- mStackSupervisor.setDockedStackMinimized(minimized);
- }
- }
-
- @Override
public void killForegroundAppsForUser(int userHandle) {
synchronized (ActivityManagerService.this) {
final ArrayList<ProcessRecord> procs = new ArrayList<>();
@@ -23542,7 +22458,7 @@
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
- if (app.persistent) {
+ if (app.isPersistent()) {
// We don't kill persistent processes.
continue;
}
@@ -23591,106 +22507,16 @@
}
@Override
- public void updatePersistentConfigurationForUser(@NonNull Configuration values,
- int userId) {
- Preconditions.checkNotNull(values, "Configuration must not be null");
- Preconditions.checkArgumentNonnegative(userId, "userId " + userId + " not supported");
- synchronized (ActivityManagerService.this) {
- updateConfigurationLocked(values, null, false, true, userId,
- false /* deferResume */);
- }
- }
-
- @Override
- public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
- Bundle bOptions) {
- Preconditions.checkNotNull(intents, "intents");
- final String[] resolvedTypes = new String[intents.length];
-
- // UID of the package on user userId.
- // "= 0" is needed because otherwise catch(RemoteException) would make it look like
- // packageUid may not be initialized.
- int packageUid = 0;
- final long ident = Binder.clearCallingIdentity();
-
- try {
- for (int i = 0; i < intents.length; i++) {
- resolvedTypes[i] =
- intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
- }
-
- packageUid = AppGlobals.getPackageManager().getPackageUid(
- packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
- } catch (RemoteException e) {
- // Shouldn't happen.
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- synchronized (ActivityManagerService.this) {
- return mActivityStartController.startActivitiesInPackage(
- packageUid, packageName,
- intents, resolvedTypes, null /* resultTo */,
- SafeActivityOptions.fromBundle(bOptions), userId,
- false /* validateIncomingUser */);
- }
- }
-
- @Override
- public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
- Intent intent, Bundle options, int userId) {
- return ActivityManagerService.this.mActivityTaskManager.startActivityAsUser(
- caller, callerPacakge, intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
- false /*validateIncomingUser*/);
- }
-
- @Override
public int getUidProcessState(int uid) {
return getUidState(uid);
}
@Override
- public void notifyKeyguardFlagsChanged(@Nullable Runnable callback) {
- synchronized (ActivityManagerService.this) {
-
- // We might change the visibilities here, so prepare an empty app transition which
- // might be overridden later if we actually change visibilities.
- final boolean wasTransitionSet =
- mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
- if (!wasTransitionSet) {
- mWindowManager.prepareAppTransition(TRANSIT_NONE,
- false /* alwaysKeepCurrent */);
- }
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-
- // If there was a transition set already we don't want to interfere with it as we
- // might be starting it too early.
- if (!wasTransitionSet) {
- mWindowManager.executeAppTransition();
- }
- }
- if (callback != null) {
- callback.run();
- }
- }
-
- @Override
public boolean isSystemReady() {
// no need to synchronize(this) just to read & return the value
return mSystemReady;
}
- @Override
- public void notifyKeyguardTrustedChanged() {
- synchronized (ActivityManagerService.this) {
- if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- }
- }
- }
-
/**
* Sets if the given pid has an overlay UI or not.
*
@@ -23718,11 +22544,6 @@
}
}
- @Override
- public void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
- ActivityManagerService.this.setRunningRemoteAnimation(pid, runningRemoteAnimation);
- }
-
/**
* Called after the network policy rules are updated by
* {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid}
@@ -23773,29 +22594,6 @@
}
@Override
- public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
- synchronized (ActivityManagerService.this) {
- mActiveVoiceInteractionServiceComponent = component;
- }
- }
-
- /**
- * Called after virtual display Id is updated by
- * {@link com.android.server.vr.Vr2dDisplay} with a specific
- * {@param vrVr2dDisplayId}.
- */
- @Override
- public void setVr2dDisplayId(int vr2dDisplayId) {
- if (DEBUG_STACK) {
- Slog.d(TAG, "setVr2dDisplayId called for: " +
- vr2dDisplayId);
- }
- synchronized (ActivityManagerService.this) {
- mVr2dDisplayId = vr2dDisplayId;
- }
- }
-
- @Override
public void saveANRState(String reason) {
synchronized (ActivityManagerService.this) {
final StringWriter sw = new StringWriter();
@@ -23805,7 +22603,7 @@
pw.println(" Reason: " + reason);
}
pw.println();
- mActivityStartController.dump(pw, " ", null);
+ mActivityTaskManager.getActivityStartController().dump(pw, " ", null);
pw.println();
pw.println("-------------------------------------------------------------------------------");
dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
@@ -23826,72 +22624,11 @@
}
@Override
- public void setFocusedActivity(IBinder token) {
- synchronized (ActivityManagerService.this) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- throw new IllegalArgumentException(
- "setFocusedActivity: No activity record matching token=" + token);
- }
- if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- r, "setFocusedActivity")) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- }
- }
- }
-
- @Override
- public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
- synchronized (ActivityManagerService.this) {
- if (mUserController.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
- if (types == null) {
- if (uid < 0) {
- return;
- }
- types = new ArrayMap<>();
- mAllowAppSwitchUids.put(userId, types);
- }
- if (uid < 0) {
- types.remove(type);
- } else {
- types.put(type, uid);
- }
- }
- }
- }
-
- @Override
public boolean isRuntimeRestarted() {
return mSystemServiceManager.isRuntimeRestarted();
}
@Override
- public boolean hasRunningActivity(int uid, @Nullable String packageName) {
- if (packageName == null) return false;
-
- synchronized (ActivityManagerService.this) {
- for (int i = 0; i < mLruProcesses.size(); i++) {
- final ProcessRecord processRecord = mLruProcesses.get(i);
- if (processRecord.uid == uid) {
- for (int j = 0; j < processRecord.activities.size(); j++) {
- final ActivityRecord activityRecord = processRecord.activities.get(j);
- if (packageName.equals(activityRecord.packageName)) {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- @Override
- public void registerScreenObserver(ScreenObserver observer) {
- mScreenObservers.add(observer);
- }
-
- @Override
public boolean canStartMoreUsers() {
return mUserController.canStartMoreUsers();
}
@@ -23912,22 +22649,6 @@
}
@Override
- public boolean isCallerRecents(int callingUid) {
- return getRecentTasks().isCallerRecents(callingUid);
- }
-
- @Override
- public boolean isRecentsComponentHomeActivity(int userId) {
- return getRecentTasks().isRecentsComponentHomeActivity(userId);
- }
-
- @Override
- public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
- ActivityManagerService.this.mActivityTaskManager.cancelRecentsAnimation(
- restoreHomeStackPosition);
- }
-
- @Override
public boolean isUidActive(int uid) {
synchronized (ActivityManagerService.this) {
return isUidActiveLocked(uid);
@@ -23935,6 +22656,11 @@
}
@Override
+ public void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
+ ActivityManagerService.this.setRunningRemoteAnimation(pid, runningRemoteAnimation);
+ }
+
+ @Override
public List<ProcessMemoryState> getMemoryStateForProcesses() {
List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
synchronized (mPidsSelfLocked) {
@@ -23962,8 +22688,84 @@
}
@Override
- public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
- ActivityManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
+ public int handleIncomingUser(int callingPid, int callingUid, int userId,
+ boolean allowAll, int allowMode, String name, String callerPackage) {
+ return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
+ allowMode, name, callerPackage);
+ }
+
+ @Override
+ public void enforceCallingPermission(String permission, String func) {
+ ActivityManagerService.this.enforceCallingPermission(permission, func);
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mUserController.getCurrentUserIdLU();
+ }
+
+ @Override
+ public boolean isUserRunning(int userId, int flags) {
+ synchronized (ActivityManagerService.this) {
+ return mUserController.isUserRunning(userId, flags);
+ }
+ }
+
+ @Override
+ public void trimApplications() {
+ ActivityManagerService.this.trimApplications();
+ }
+
+ public int getPackageScreenCompatMode(ApplicationInfo ai) {
+ synchronized (ActivityManagerService.this) {
+ return mCompatModePackages.computeCompatModeLocked(ai);
+ }
+ }
+
+ public void setPackageScreenCompatMode(ApplicationInfo ai, int mode) {
+ synchronized (ActivityManagerService.this) {
+ mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
+ }
+ }
+
+ public void closeSystemDialogs(String reason) {
+ ActivityManagerService.this.closeSystemDialogs(reason);
+ }
+
+ public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < procsToKill.size(); i++) {
+ final WindowProcessController wpc =
+ (WindowProcessController) procsToKill.get(i);
+ final ProcessRecord pr = (ProcessRecord) wpc.mOwner;
+ if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+ && pr.curReceivers.isEmpty()) {
+ pr.kill("remove task", true);
+ } else {
+ // We delay killing processes that are not in the background or running a
+ // receiver.
+ pr.waitingToKill = "remove task";
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean hasRunningActivity(int uid, @Nullable String packageName) {
+ if (packageName == null) return false;
+
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < mLruProcesses.size(); i++) {
+ final ProcessRecord pr = mLruProcesses.get(i);
+ if (pr.uid != uid) {
+ continue;
+ }
+ if (pr.getWindowProcessController().hasRunningActivity(packageName)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0e78d06..e6c3475 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2760,7 +2760,7 @@
int runWrite(PrintWriter pw) {
mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerUidObserver()");
- mInternal.getRecentTasks().flush();
+ mInternal.mActivityTaskManager.getRecentTasks().flush();
pw.println("All tasks persisted.");
return 0;
}
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 47d0423..9b08823 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -2,7 +2,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -154,7 +154,7 @@
launchedActivityLaunchToken = info.launchedActivity.info.launchToken;
launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null
? null
- : info.launchedActivity.app.requiredAbi;
+ : info.launchedActivity.app.getRequiredAbi();
reason = info.reason;
startingWindowDelayMs = info.startingWindowDelayMs;
bindApplicationDelayMs = info.bindApplicationDelayMs;
@@ -185,6 +185,10 @@
mWindowState = WINDOW_STATE_INVALID;
ActivityStack stack = mSupervisor.getFocusedStack();
+ if (stack == null) {
+ return;
+ }
+
if (stack.isActivityTypeAssistant()) {
mWindowState = WINDOW_STATE_ASSISTANT;
return;
@@ -242,25 +246,11 @@
// of caches might be purged so the time until it produces the first frame is very
// interesting.
final boolean processSwitch = processRecord == null
- || !hasStartedActivity(processRecord, launchedActivity);
+ || !processRecord.getWindowProcessController().hasStartedActivity(launchedActivity);
notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
}
- private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
- final ArrayList<ActivityRecord> activities = record.activities;
- for (int i = activities.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = activities.get(i);
- if (launchedActivity == activity) {
- continue;
- }
- if (!activity.stopped) {
- return true;
- }
- }
- return false;
- }
-
/**
* Notifies the tracker the the activity is actually launching.
*
@@ -407,7 +397,7 @@
}
private void checkVisibility(TaskRecord t, ActivityRecord r) {
- synchronized (mSupervisor.mService) {
+ synchronized (mSupervisor.mService.mGlobalLock) {
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
r.getWindowingMode());
@@ -663,8 +653,8 @@
private ProcessRecord findProcessForActivity(ActivityRecord launchedActivity) {
return launchedActivity != null
- ? mSupervisor.mService.mProcessNames.get(launchedActivity.processName,
- launchedActivity.appInfo.uid)
+ ? mSupervisor.mService.mAm.mProcessNames.get(
+ launchedActivity.processName, launchedActivity.appInfo.uid)
: null;
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 35f3c09..d40b9b4 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -230,7 +230,7 @@
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
- final ActivityManagerService service; // owner
+ final ActivityTaskManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
AppWindowContainerController mWindowContainerController;
final ActivityInfo info; // all about me
@@ -288,7 +288,7 @@
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
- ProcessRecord app; // if non-null, hosting application
+ WindowProcessController app; // if non-null, hosting application
private ActivityState mState; // current state we are in
Bundle icicle; // last saved activity state
PersistableBundle persistentState; // last persistently saved activity state
@@ -622,7 +622,7 @@
}
private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
"Can't report activity moved to display - client not running, activityRecord="
+ this + ", displayId=" + displayId);
@@ -633,7 +633,7 @@
"Reporting activity moved to display" + ", activityRecord=" + this
+ ", displayId=" + displayId + ", config=" + config);
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MoveToDisplayItem.obtain(displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -641,7 +641,7 @@
}
private void scheduleConfigurationChanged(Configuration config) {
- if (app == null || app.thread == null) {
+ if (attachedToProcess()) {
if (DEBUG_CONFIGURATION) Slog.w(TAG,
"Can't report activity configuration update - client not running"
+ ", activityRecord=" + this);
@@ -651,7 +651,7 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+ config);
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
ActivityConfigurationChangeItem.obtain(config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -659,7 +659,7 @@
}
void updateMultiWindowMode() {
- if (task == null || task.getStack() == null || app == null || app.thread == null) {
+ if (task == null || task.getStack() == null || !attachedToProcess()) {
return;
}
@@ -678,16 +678,15 @@
private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
- MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode,
- overrideConfig));
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
} catch (Exception e) {
// If process died, I don't care.
}
}
void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
- if (task == null || task.getStack() == null || app == null || app.thread == null) {
+ if (task == null || task.getStack() == null || !attachedToProcess()) {
return;
}
@@ -706,7 +705,7 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
overrideConfig));
} catch (Exception e) {
@@ -847,7 +846,7 @@
}
}
- ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
+ ActivityRecord(ActivityTaskManagerService _service, ProcessRecord _caller, int _launchedFromPid,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
@@ -985,7 +984,7 @@
}
}
- void setProcess(ProcessRecord proc) {
+ void setProcess(WindowProcessController proc) {
app = proc;
final ActivityRecord root = task != null ? task.getRootActivity() : null;
if (root == this) {
@@ -993,6 +992,14 @@
}
}
+ boolean hasProcess() {
+ return app != null;
+ }
+
+ boolean attachedToProcess() {
+ return hasProcess() && app.hasThread();
+ }
+
AppWindowContainerController getWindowContainerController() {
return mWindowContainerController;
}
@@ -1111,7 +1118,8 @@
* @return whether the given package name can launch an assist activity.
*/
private boolean canLaunchAssistActivity(String packageName) {
- final ComponentName assistComponent = service.mActiveVoiceInteractionServiceComponent;
+ final ComponentName assistComponent =
+ service.mActiveVoiceInteractionServiceComponent;
if (assistComponent != null) {
return assistComponent.getPackageName().equals(packageName);
}
@@ -1131,8 +1139,8 @@
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
- } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) ||
- service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+ } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
+ || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
@@ -1293,7 +1301,8 @@
}
boolean isKeyguardLocked = service.isKeyguardLocked();
- boolean isCurrentAppLocked = service.mActivityTaskManager.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ boolean isCurrentAppLocked =
+ service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
final ActivityDisplay display = getDisplay();
boolean hasPinnedStack = display != null && display.hasPinnedStack();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
@@ -1335,8 +1344,8 @@
*/
private boolean checkEnterPictureInPictureAppOpsState() {
try {
- return service.getAppOpsService().checkOperation(OP_PICTURE_IN_PICTURE,
- appInfo.uid, packageName) == MODE_ALLOWED;
+ return service.mAm.getAppOpsService().checkOperation(
+ OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
} catch (RemoteException e) {
// Local call
}
@@ -1366,13 +1375,13 @@
}
if (service != null) {
- service.mTaskChangeNotificationController.notifyTaskStackChanged();
+ service.getTaskChangeNotificationController().notifyTaskStackChanged();
}
}
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
- uriPermissions = new UriPermissionOwner(service, this);
+ uriPermissions = new UriPermissionOwner(service.mAm, this);
}
return uriPermissions;
}
@@ -1415,7 +1424,7 @@
final boolean isSleeping() {
final ActivityStack stack = getStack();
- return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
+ return stack != null ? stack.shouldSleepActivities() : service.mAm.isSleepingLocked();
}
/**
@@ -1424,7 +1433,7 @@
*/
final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
// The activity now gets access to the data associated with this Intent.
- service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+ service.mAm.grantUriPermissionFromIntentLocked(callingUid, packageName,
intent, getUriPermissionsLocked(), userId);
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
@@ -1434,13 +1443,13 @@
// - It is currently resumed or paused. i.e. it is currently visible to the user and we want
// the user to see the visual effects caused by the intent delivery now.
// - The device is sleeping and it is the top activity behind the lock screen (b/6700897).
- if ((mState == RESUMED || mState == PAUSED
- || isTopActivityWhileSleeping) && app != null && app.thread != null) {
+ if ((mState == RESUMED || mState == PAUSED || isTopActivityWhileSleeping)
+ && attachedToProcess()) {
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
- NewIntentItem.obtain(ar, mState == PAUSED));
+ service.getLifecycleManager().scheduleTransaction(
+ app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
unsent = false;
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
@@ -1743,7 +1752,7 @@
}
setVisible(true);
sleeping = false;
- app.pendingUiClean = true;
+ app.setPendingUiClean(true);
if (reportToClient) {
makeClientVisible();
} else {
@@ -1763,13 +1772,13 @@
void makeClientVisible() {
mClientVisibilityDeferred = false;
try {
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
if (shouldPauseWhenBecomingVisible()) {
// An activity must be in the {@link PAUSING} state for the system to validate
// the move to {@link PAUSED}.
setState(PAUSING, "makeVisibleIfNeeded");
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */));
}
@@ -1815,7 +1824,7 @@
stopFreezingScreenLocked(false);
try {
if (returningOptions != null) {
- app.thread.scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
+ app.getThread().scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
}
} catch(RemoteException e) {
}
@@ -1849,8 +1858,8 @@
stopped = false;
if (isActivityTypeHome()) {
- ProcessRecord app = task.mActivities.get(0).app;
- if (app != null && app != service.mHomeProcess) {
+ WindowProcessController app = task.mActivities.get(0).app;
+ if (hasProcess() && app != service.mHomeProcess) {
service.mHomeProcess = app;
}
}
@@ -1872,8 +1881,8 @@
// Mark the point when the activity is resuming
// TODO: To be more accurate, the mark should be before the onCreate,
// not after the onResume. But for subsequent starts, onResume is fine.
- if (app != null) {
- cpuTimeAtResume = service.mProcessCpuTracker.getCpuTimeForPid(app.pid);
+ if (hasProcess()) {
+ cpuTimeAtResume = service.mAm.mProcessCpuTracker.getCpuTimeForPid(app.getPid());
} else {
cpuTimeAtResume = 0; // Couldn't get the cpu time of process
}
@@ -1969,15 +1978,15 @@
// IApplicationToken
- public boolean mayFreezeScreenLocked(ProcessRecord app) {
+ public boolean mayFreezeScreenLocked(WindowProcessController app) {
// Only freeze the screen if this activity is currently attached to
// an application, and that application is not blocked or unresponding.
// In any other case, we can't count on getting the screen unfrozen,
// so it is best to leave as-is.
- return app != null && !app.crashing && !app.notResponding;
+ return hasProcess() && !app.isCrashing() && !app.isNotResponding();
}
- public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
+ public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
mWindowContainerController.startFreezingScreen(configChanges);
}
@@ -2006,7 +2015,7 @@
EventLog.writeEvent(AM_ACTIVITY_FULLY_DRAWN_TIME,
userId, System.identityHashCode(this), shortComponentName,
thisTime, totalTime);
- StringBuilder sb = service.mStringBuilder;
+ StringBuilder sb = service.mAm.mStringBuilder;
sb.setLength(0);
sb.append("Fully drawn ");
sb.append(shortComponentName);
@@ -2043,7 +2052,7 @@
EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME,
userId, System.identityHashCode(this), shortComponentName,
thisTime, totalTime);
- StringBuilder sb = service.mStringBuilder;
+ StringBuilder sb = service.mAm.mStringBuilder;
sb.setLength(0);
sb.append("Displayed ");
sb.append(shortComponentName);
@@ -2066,7 +2075,7 @@
@Override
public void onStartingWindowDrawn(long timestamp) {
- synchronized (service) {
+ synchronized (service.mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
getWindowingMode(), timestamp);
}
@@ -2074,7 +2083,7 @@
@Override
public void onWindowsDrawn(long timestamp) {
- synchronized (service) {
+ synchronized (service.mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(),
timestamp);
if (displayStartTime != 0) {
@@ -2091,7 +2100,7 @@
@Override
public void onWindowsVisible() {
- synchronized (service) {
+ synchronized (service.mGlobalLock) {
mStackSupervisor.reportActivityVisibleLocked(this);
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
if (!nowVisible) {
@@ -2118,14 +2127,14 @@
mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
false /* remove */, true /* processPausingActivities */);
}
- service.scheduleAppGcsLocked();
+ service.mAm.scheduleAppGcsLocked();
}
}
}
@Override
public void onWindowsGone() {
- synchronized (service) {
+ synchronized (service.mGlobalLock) {
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
nowVisible = false;
}
@@ -2134,20 +2143,22 @@
@Override
public boolean keyDispatchingTimedOut(String reason, int windowPid) {
ActivityRecord anrActivity;
- ProcessRecord anrApp;
+ WindowProcessController anrApp;
boolean windowFromSameProcessAsActivity;
- synchronized (service) {
+ synchronized (service.mGlobalLock) {
anrActivity = getWaitingHistoryRecordLocked();
anrApp = app;
windowFromSameProcessAsActivity =
- app == null || app.pid == windowPid || windowPid == -1;
+ !hasProcess() || app.getPid() == windowPid || windowPid == -1;
}
if (windowFromSameProcessAsActivity) {
- return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
+ return service.mAm.inputDispatchingTimedOut(
+ (ProcessRecord) anrApp.mOwner, anrActivity, this, false, reason);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
- return service.inputDispatchingTimedOut(windowPid, false /* aboveSystem */, reason) < 0;
+ return service.mAm.inputDispatchingTimedOut(
+ windowPid, false /* aboveSystem */, reason) < 0;
}
}
@@ -2180,7 +2191,7 @@
return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
|| (mStackSupervisor.isCurrentProfileLocked(userId)
- && service.mUserController.isUserRunning(userId, 0 /* flags */));
+ && service.mAm.mUserController.isUserRunning(userId, 0 /* flags */));
}
/**
@@ -2199,9 +2210,9 @@
if (!force && sleeping == _sleeping) {
return;
}
- if (app != null && app.thread != null) {
+ if (attachedToProcess()) {
try {
- app.thread.scheduleSleeping(appToken, _sleeping);
+ app.getThread().scheduleSleeping(appToken, _sleeping);
if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
mStackSupervisor.mGoingToSleepActivities.add(this);
}
@@ -2250,7 +2261,7 @@
}
final boolean isDestroyable() {
- if (finishing || app == null) {
+ if (finishing || !hasProcess()) {
// This would be redundant.
return false;
}
@@ -2311,7 +2322,7 @@
}
final CompatibilityInfo compatInfo =
- service.compatibilityInfoForPackageLocked(info.applicationInfo);
+ service.mAm.compatibilityInfoForPackageLocked(info.applicationInfo);
final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
@@ -2349,7 +2360,7 @@
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
- service.mTaskChangeNotificationController.notifyActivityRequestedOrientationChanged(
+ service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
task.taskId, requestedOrientation);
}
@@ -2559,7 +2570,8 @@
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+ setLastReportedConfiguration(service.mAm.getGlobalConfiguration(),
+ newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -2589,7 +2601,7 @@
// If the activity isn't currently running, just leave the new configuration and it will
// pick that up next time it starts.
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration doesn't matter not running " + this);
stopFreezingScreenLocked(false);
@@ -2610,7 +2622,7 @@
startFreezingScreenLocked(app, globalChanges);
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is destroying non-running " + this);
stack.destroyActivityLocked(this, true, "config");
@@ -2732,7 +2744,7 @@
}
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
- if (service.mActivityTaskManager.mSuppressResizeConfigChanges && preserveWindow) {
+ if (service.mSuppressResizeConfigChanges && preserveWindow) {
configChangeFlags = 0;
return;
}
@@ -2761,7 +2773,7 @@
mStackSupervisor.activityRelaunchingLocked(this);
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(service.getGlobalConfiguration(),
+ new MergedConfiguration(service.mAm.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
@@ -2770,7 +2782,7 @@
} else {
lifecycleItem = PauseActivityItem.obtain();
}
- final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
+ final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
service.getLifecycleManager().scheduleTransaction(transaction);
@@ -2787,10 +2799,10 @@
}
results = null;
newIntents = null;
- service.getAppWarningsLocked().onResumeActivity(this);
- service.showAskCompatModeDialogLocked(this);
+ service.mAm.getAppWarningsLocked().onResumeActivity(this);
+ service.mAm.showAskCompatModeDialogLocked(this);
} else {
- service.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+ service.mAm.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
setState(PAUSED, "relaunchActivityLocked");
}
@@ -2800,11 +2812,11 @@
}
private boolean isProcessRunning() {
- ProcessRecord proc = app;
+ WindowProcessController proc = app;
if (proc == null) {
proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
}
- return proc != null && proc.thread != null;
+ return proc != null && proc.hasThread();
}
/**
@@ -2926,7 +2938,7 @@
throw new XmlPullParserException("restoreActivity error intent=" + intent);
}
- final ActivityManagerService service = stackSupervisor.mService;
+ final ActivityTaskManagerService service = stackSupervisor.mService;
final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
userId, Binder.getCallingUid());
if (aInfo == null) {
@@ -2935,7 +2947,7 @@
}
final ActivityRecord r = new ActivityRecord(service, null /* caller */,
0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
- aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
+ aInfo, service.mAm.getConfiguration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
stackSupervisor, null /* options */, null /* sourceRecord */);
@@ -3033,8 +3045,8 @@
proto.write(STATE, mState.toString());
proto.write(VISIBLE, visible);
proto.write(FRONT_OF_TASK, frontOfTask);
- if (app != null) {
- proto.write(PROC_ID, app.pid);
+ if (hasProcess()) {
+ proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !fullscreen);
proto.end(token);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 88589cc..9b01919 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -271,7 +271,7 @@
// the input bounds right or bottom side minus the width or height divided by this value.
private static final int FIT_WITHIN_BOUNDS_DIVIDER = 3;
- final ActivityManagerService mService;
+ final ActivityTaskManagerService mService;
private final WindowManagerService mWindowManager;
T mWindowContainerController;
@@ -367,9 +367,9 @@
static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
private static class ScheduleDestroyArgs {
- final ProcessRecord mOwner;
+ final WindowProcessController mOwner;
final String mReason;
- ScheduleDestroyArgs(ProcessRecord owner, String reason) {
+ ScheduleDestroyArgs(WindowProcessController owner, String reason) {
mOwner = owner;
mReason = reason;
}
@@ -391,8 +391,8 @@
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity pause timeout for " + r);
- synchronized (mService) {
- if (r.app != null) {
+ synchronized (mService.mGlobalLock) {
+ if (r.hasProcess()) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
activityPausedLocked(r.appToken, true);
@@ -400,7 +400,7 @@
} break;
case LAUNCH_TICK_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (r.continueLaunchTickingLocked()) {
mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
}
@@ -411,7 +411,7 @@
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity destroy timeout for " + r);
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
}
} break;
@@ -420,7 +420,7 @@
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity stop timeout for " + r);
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (r.isInHistory()) {
r.activityStoppedLocked(null /* icicle */,
null /* persistentState */, null /* description */);
@@ -429,12 +429,12 @@
} break;
case DESTROY_ACTIVITIES_MSG: {
ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
destroyActivitiesLocked(args.mOwner, args.mReason);
}
} break;
case TRANSLUCENT_TIMEOUT_MSG: {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
notifyActivityDrawnLocked(null);
}
} break;
@@ -454,10 +454,10 @@
int windowingMode, int activityType, boolean onTop) {
mStackSupervisor = supervisor;
mService = supervisor.mService;
- mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
+ mHandler = new ActivityStackHandler(supervisor.mLooper);
mWindowManager = mService.mWindowManager;
mStackId = stackId;
- mCurrentUser = mService.mUserController.getCurrentUserId();
+ mCurrentUser = mService.mAm.mUserController.getCurrentUserId();
mTmpRect2.setEmpty();
// Set display id before setting activity and window type to make sure it won't affect
// stacks on a wrong display.
@@ -495,7 +495,7 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+ reason);
setResumedActivity(record, reason + " - onActivityStateChanged");
- mService.setResumedActivityUncheckLocked(record, reason);
+ mService.mAm.setResumedActivityUncheckLocked(record, reason);
mStackSupervisor.mRecentTasks.add(record.getTask());
}
}
@@ -552,7 +552,7 @@
// Looks like we can't launch in split screen mode or the stack we are launching
// doesn't support split-screen mode, go ahead an dismiss split-screen and display a
// warning toast about it.
- mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN,
false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
@@ -573,7 +573,7 @@
// Inform the user that they are starting an app that may not work correctly in
// multi-window mode.
final String packageName = topActivity.appInfo.packageName;
- mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+ mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
}
@@ -1451,17 +1451,17 @@
mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
- mService.updateCpuStats();
+ mService.mAm.updateCpuStats();
- if (prev.app != null && prev.app.thread != null) {
+ if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
- mService.updateUsageStats(prev, false);
+ mService.mAm.updateUsageStats(prev, false);
- mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
- PauseActivityItem.obtain(prev.finishing, userLeaving,
+ mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
@@ -1478,7 +1478,7 @@
// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
- if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
+ if (!uiSleeping && !mService.mAm.isSleepingOrShuttingDownLocked()) {
mStackSupervisor.acquireLaunchWakelock();
}
@@ -1563,7 +1563,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
"completedPausedLocked");
- } else if (prev.app != null) {
+ } else if (prev.hasProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ " wasStopping=" + wasStopping + " visible=" + prev.visible);
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {
@@ -1620,12 +1620,12 @@
if (prev != null) {
prev.resumeKeyDispatchingLocked();
- if (prev.app != null && prev.cpuTimeAtResume > 0
- && mService.mBatteryStatsService.isOnBattery()) {
- long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
+ if (prev.hasProcess() && prev.cpuTimeAtResume > 0
+ && mService.mAm.mBatteryStatsService.isOnBattery()) {
+ long diff = mService.mAm.mProcessCpuTracker.getCpuTimeForPid(prev.app.getPid())
- prev.cpuTimeAtResume;
if (diff > 0) {
- BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+ BatteryStatsImpl bsi = mService.mAm.mBatteryStatsService.getActiveStatistics();
synchronized (bsi) {
BatteryStatsImpl.Uid.Proc ps =
bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
@@ -1644,7 +1644,7 @@
// task stack changes, because its positioning may depend on it.
if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
|| getDisplay().hasPinnedStack()) {
- mService.mTaskChangeNotificationController.notifyTaskStackChanged();
+ mService.getTaskChangeNotificationController().notifyTaskStackChanged();
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
@@ -1904,7 +1904,7 @@
true /* ignoreStopState */);
}
- if (r.app == null || r.app.thread == null) {
+ if (!r.attachedToProcess()) {
if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
resumeNextActivity, r)) {
if (activityNdx >= activities.size()) {
@@ -2136,11 +2136,11 @@
switch (r.getState()) {
case STOPPING:
case STOPPED:
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Scheduling invisibility: " + r);
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
- WindowVisibilityItem.obtain(false /* showWindow */));
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
+ r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
}
// Reset the flag indicating that an app can enter picture-in-picture once the
@@ -2217,9 +2217,9 @@
if (waitingActivity != null) {
mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
- if (waitingActivity.app != null && waitingActivity.app.thread != null) {
+ if (waitingActivity.attachedToProcess()) {
try {
- waitingActivity.app.thread.scheduleTranslucentConversionComplete(
+ waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
waitingActivity.appToken, r != null);
} catch (RemoteException e) {
}
@@ -2328,7 +2328,7 @@
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
- if (!mService.mBooting && !mService.mBooted) {
+ if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
// Not ready yet!
return false;
}
@@ -2388,7 +2388,7 @@
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
- if (!mService.mUserController.hasStartedUserState(next.userId)) {
+ if (!mService.mAm.mUserController.hasStartedUserState(next.userId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2451,8 +2451,9 @@
// at the top of the LRU list, since we know we will be needing it
// very soon and it would be a waste to let it get killed if it
// happens to be sitting towards the end.
- if (next.app != null && next.app.thread != null) {
- mService.updateLruProcessLocked(next.app, true, null);
+ if (next.attachedToProcess()) {
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (lastResumed != null) {
@@ -2577,7 +2578,7 @@
mStackSupervisor.mNoAnimActivities.clear();
ActivityStack lastStack = mStackSupervisor.getLastStack();
- if (next.app != null && next.app.thread != null) {
+ if (next.attachedToProcess()) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
+ " stopped=" + next.stopped + " visible=" + next.visible);
@@ -2611,16 +2612,16 @@
lastStack == null ? null :lastStack.mResumedActivity;
final ActivityState lastState = next.getState();
- mService.updateCpuStats();
+ mService.mAm.updateCpuStats();
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ " (in existing)");
next.setState(RESUMED, "resumeTopActivityInnerLocked");
- mService.updateLruProcessLocked(next.app, true, null);
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* updateLru */, true /* activityChange */, true /* updateOomAdj */);
updateLRUListLocked(next);
- mService.updateOomAdjLocked();
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
@@ -2662,8 +2663,8 @@
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
- next.appToken);
+ final ClientTransaction transaction =
+ ClientTransaction.obtain(next.app.getThread(), next.appToken);
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -2689,13 +2690,13 @@
next.shortComponentName);
next.sleeping = false;
- mService.getAppWarningsLocked().onResumeActivity(next);
- mService.showAskCompatModeDialogLocked(next);
- next.app.pendingUiClean = true;
- next.app.forceProcessStateUpTo(mService.mTopProcessState);
+ mService.mAm.getAppWarningsLocked().onResumeActivity(next);
+ mService.mAm.showAskCompatModeDialogLocked(next);
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(
+ mService.mAm.mTopProcessState);
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.app.repProcState,
+ ResumeActivityItem.obtain(next.app.getReportedProcState(),
mService.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
@@ -3338,19 +3339,19 @@
String resultWho, int requestCode, int resultCode, Intent data) {
if (callingUid > 0) {
- mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ mService.mAm.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
data, r.getUriPermissionsLocked(), r.userId);
}
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ " : who=" + resultWho + " req=" + requestCode
+ " res=" + resultCode + " data=" + data);
- if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+ if (mResumedActivity == r && r.attachedToProcess()) {
try {
ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
list.add(new ResultInfo(resultWho, requestCode,
resultCode, data));
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
ActivityResultItem.obtain(list));
return;
} catch (Exception e) {
@@ -3464,7 +3465,7 @@
}
}
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
adjustFocusedActivityStack(r, "stopActivity");
r.resumeKeyDispatchingLocked();
try {
@@ -3479,7 +3480,7 @@
}
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
StopActivityItem.obtain(r.visible, r.configChangeFlags));
if (shouldSleepOrShutDownActivities()) {
r.setSleeping(true);
@@ -3535,7 +3536,7 @@
}
}
}
- mService.updateOomAdjLocked();
+ mService.mAm.updateOomAdjLocked();
}
/**
@@ -3546,7 +3547,7 @@
* @return The task that was finished in this stack, {@code null} if top running activity does
* not belong to the crashed app.
*/
- final TaskRecord finishTopCrashedActivityLocked(ProcessRecord app, String reason) {
+ final TaskRecord finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
ActivityRecord r = topRunningActivityLocked();
TaskRecord finishedTask = null;
if (r == null || r.app != app) {
@@ -3606,17 +3607,16 @@
// Check if any of the activities are using voice
for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = tr.mActivities.get(activityNdx);
- if (r.voiceSession != null
- && r.voiceSession.asBinder() == sessionBinder) {
+ if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
// Inform of cancellation
r.clearVoiceSessionLocked();
try {
- r.app.thread.scheduleLocalVoiceInteractionStarted((IBinder) r.appToken,
- null);
+ r.app.getThread().scheduleLocalVoiceInteractionStarted(
+ r.appToken, null);
} catch (RemoteException re) {
// Ok
}
- mService.finishRunningVoiceLocked();
+ mService.mAm.finishRunningVoiceLocked();
break;
}
}
@@ -3624,7 +3624,7 @@
}
if (didOne) {
- mService.updateOomAdjLocked();
+ mService.mAm.updateOomAdjLocked();
}
}
@@ -3653,12 +3653,11 @@
}
}
if (r.info.applicationInfo.uid > 0) {
- mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+ mService.mAm.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
resultTo.packageName, resultData,
resultTo.getUriPermissionsLocked(), resultTo.userId);
}
- resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
- resultData);
+ resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
r.resultTo = null;
}
else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r);
@@ -3723,7 +3722,7 @@
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: finishing " + r);
if (endTask) {
- mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(
+ mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
task.taskId);
}
mWindowManager.prepareAppTransition(transit, false);
@@ -3814,7 +3813,7 @@
"Moving to STOPPING: "+ r + " (finish requested)");
r.setState(STOPPING, "finishCurrentActivityLocked");
if (oomAdj) {
- mService.updateOomAdjLocked();
+ mService.mAm.updateOomAdjLocked();
}
return r;
}
@@ -3957,6 +3956,8 @@
}
}
+ // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
+ // We should consolidate.
IActivityController controller = mService.mController;
if (controller != null) {
ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
@@ -4001,7 +4002,7 @@
// TODO(b/64750076): Check if calling pid should really be -1.
final int res = mService.getActivityStartController()
.obtainStarter(destIntent, "navigateUpTo")
- .setCaller(srec.app.thread)
+ .setCaller(srec.app.getThread())
.setActivityInfo(aInfo)
.setResultTo(parent.appToken)
.setCallingPid(-1)
@@ -4075,7 +4076,7 @@
for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
PendingIntentRecord rec = apr.get();
if (rec != null) {
- mService.cancelIntentSenderLocked(rec, false);
+ mService.mAm.cancelIntentSenderLocked(rec, false);
}
}
r.pendingResults = null;
@@ -4162,19 +4163,19 @@
Iterator<ConnectionRecord> it = r.connections.iterator();
while (it.hasNext()) {
ConnectionRecord c = it.next();
- mService.mServices.removeConnectionLocked(c, null, r);
+ mService.mAm.mServices.removeConnectionLocked(c, null, r);
}
r.connections = null;
}
}
- final void scheduleDestroyActivities(ProcessRecord owner, String reason) {
+ final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
msg.obj = new ScheduleDestroyArgs(owner, reason);
mHandler.sendMessage(msg);
}
- private void destroyActivitiesLocked(ProcessRecord owner, String reason) {
+ private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
boolean lastIsOpaque = false;
boolean activityRemoved = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -4219,7 +4220,7 @@
return false;
}
- final int releaseSomeActivitiesLocked(ProcessRecord app, ArraySet<TaskRecord> tasks,
+ final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
String reason) {
// Iterate over tasks starting at the back (oldest) first.
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
@@ -4273,7 +4274,7 @@
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
"Removing activity from " + reason + ": token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
if (r.isState(DESTROYING, DESTROYED)) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
@@ -4289,23 +4290,23 @@
cleanUpActivityLocked(r, false, false);
- final boolean hadApp = r.app != null;
+ final boolean hadApp = r.hasProcess();
if (hadApp) {
if (removeFromApp) {
- r.app.activities.remove(r);
- if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
- mService.mHeavyWeightProcess = null;
- mService.mHandler.sendEmptyMessage(
+ r.app.removeActivity(r);
+ if (mService.mAm.mHeavyWeightProcess != null
+ && mService.mAm.mHeavyWeightProcess.getWindowProcessController() == r.app
+ && !r.app.hasActivities()) {
+ mService.mAm.mHeavyWeightProcess = null;
+ mService.mAm.mHandler.sendEmptyMessage(
ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
}
- if (r.app.activities.isEmpty()) {
+ if (!r.app.hasActivities()) {
// Update any services we are bound to that might care about whether
// their client may have activities.
- mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
// No longer have activities, so update LRU list and oom adj.
- mService.updateLruProcessLocked(r.app, false, null);
- mService.updateOomAdjLocked();
+ r.app.updateProcessInfo(true, true, false, true);
}
}
@@ -4313,7 +4314,7 @@
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
} catch (Exception e) {
// We can just ignore exceptions here... if the process
@@ -4404,7 +4405,7 @@
}
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
- ProcessRecord app, String listName) {
+ WindowProcessController app, String listName) {
int i = list.size();
if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing app " + app + " from list " + listName + " with " + i + " entries");
@@ -4420,7 +4421,7 @@
}
}
- private boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
+ private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
"mStoppingActivities");
@@ -4483,7 +4484,7 @@
r.getTask().taskId, r.shortComponentName,
"proc died without state saved");
if (r.getState() == RESUMED) {
- mService.updateUsageStats(r, false);
+ mService.mAm.updateUsageStats(r, false);
}
}
} else {
@@ -4629,7 +4630,7 @@
mStackSupervisor.resumeFocusedStackTopActivityLocked();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
- mService.mTaskChangeNotificationController.notifyTaskMovedToFront(tr.taskId);
+ mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
} finally {
getDisplay().continueUpdateImeTarget();
}
@@ -4751,7 +4752,7 @@
// TODO: Figure-out a way to consolidate with resize() method below.
@Override
public void requestResize(Rect bounds) {
- mService.mActivityTaskManager.resizeStack(mStackId, bounds,
+ mService.resizeStack(mStackId, bounds,
true /* allowResizeInDockedMode */, false /* preserveWindows */,
false /* animate */, -1 /* animationDuration */);
}
@@ -4894,7 +4895,7 @@
|| (packageName == null && r.userId == userId);
if ((userId == UserHandle.USER_ALL || r.userId == userId)
&& (sameComponent || r.getTask() == lastTask)
- && (r.app == null || evenPersistent || !r.app.persistent)) {
+ && (r.app == null || evenPersistent || !r.app.isPersistent())) {
if (!doit) {
if (r.finishing) {
// If this activity is just finishing, then it is not
@@ -4914,8 +4915,8 @@
didSomething = true;
Slog.i(TAG, " Force finishing activity " + r);
if (sameComponent) {
- if (r.app != null) {
- r.app.removed = true;
+ if (r.hasProcess()) {
+ r.app.setRemoved(true);
}
r.app = null;
}
@@ -4986,7 +4987,7 @@
* @param app The app of the activity that died.
* @return result from removeHistoryRecordsForAppLocked.
*/
- boolean handleAppDiedLocked(ProcessRecord app) {
+ boolean handleAppDiedLocked(WindowProcessController app) {
if (mPausingActivity != null && mPausingActivity.app == app) {
if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
"App died while pausing: " + mPausingActivity);
@@ -5000,7 +5001,7 @@
return removeHistoryRecordsForAppLocked(app);
}
- void handleAppCrashLocked(ProcessRecord app) {
+ void handleAppCrashLocked(WindowProcessController app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5160,7 +5161,7 @@
// Notify if a task from the pinned stack is being removed (or moved depending on the mode)
if (inPinnedWindowingMode()) {
- mService.mTaskChangeNotificationController.notifyActivityUnpinned();
+ mService.getTaskChangeNotificationController().notifyActivityUnpinned();
}
}
@@ -5337,11 +5338,11 @@
return false;
}
- return display != null ? display.isSleeping() : mService.isSleepingLocked();
+ return display != null ? display.isSleeping() : mService.mAm.isSleepingLocked();
}
boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mService.isShuttingDownLocked();
+ return shouldSleepActivities() || mService.mAm.isShuttingDownLocked();
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 615edd5..f49943e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -51,7 +51,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
@@ -110,7 +109,9 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManagerInternal.SleepToken;
+import android.app.ActivityManagerInternal;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.ProfilerInfo;
@@ -305,7 +306,7 @@
/** The number of distinct task ids that can be assigned to the tasks of a single user */
private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
- ActivityManagerService mService;
+ ActivityTaskManagerService mService;
/** The historial list of recent tasks including inactive tasks */
RecentTasks mRecentTasks;
@@ -319,7 +320,6 @@
/** Short cut */
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
- ActivityTaskManagerService mAtm;
private LaunchParamsController mLaunchParamsController;
@@ -582,21 +582,21 @@
final ActivityRecord sourceRecord;
final int startFlags;
final ActivityStack stack;
- final ProcessRecord callerApp;
+ final WindowProcessController callerApp;
PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
- int _startFlags, ActivityStack _stack, ProcessRecord _callerApp) {
+ int _startFlags, ActivityStack _stack, WindowProcessController app) {
r = _r;
sourceRecord = _sourceRecord;
startFlags = _startFlags;
stack = _stack;
- callerApp = _callerApp;
+ callerApp = app;
}
void sendErrorResult(String message) {
try {
- if (callerApp.thread != null) {
- callerApp.thread.scheduleCrash(message);
+ if (callerApp.hasThread()) {
+ callerApp.getThread().scheduleCrash(message);
}
} catch (RemoteException e) {
Slog.e(TAG, "Exception scheduling crash of failed "
@@ -605,14 +605,14 @@
}
}
- public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+ public ActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
mService = service;
mLooper = looper;
mHandler = new ActivityStackSupervisorHandler(looper);
}
@VisibleForTesting
- void setService(ActivityManagerService service) {
+ void setService(ActivityTaskManagerService service) {
mService = service;
}
@@ -623,9 +623,8 @@
mInitialized = true;
mRunningTasks = createRunningTasks();
- mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
- mHandler.getLooper());
- mKeyguardController = new KeyguardController(mService, this);
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
+ mKeyguardController = new KeyguardController(mService.mAm, this);
mLaunchParamsController = new LaunchParamsController(mService);
mLaunchParamsController.registerDefaultModifiers(this);
@@ -666,10 +665,6 @@
mLaunchingActivity.setReferenceCounted(false);
}
- void setActivityTaskManager(ActivityTaskManagerService atm) {
- mAtm = atm;
- }
-
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
getKeyguardController().setWindowManager(wm);
@@ -716,6 +711,11 @@
if (!focusCandidate.isFocusable()) {
// The focus candidate isn't focusable. Move focus to the top stack that is focusable.
focusCandidate = getNextFocusableStackLocked(focusCandidate, false /* ignoreCurrent */);
+ if (focusCandidate == null) {
+ Slog.w(TAG,
+ "setFocusStackUnchecked: No focusable stack found, focus home as default");
+ focusCandidate = mHomeStack;
+ }
}
if (focusCandidate != mFocusedStack) {
@@ -728,7 +728,7 @@
}
final ActivityRecord r = topRunningActivityLocked();
- if (mService.mBooting || !mService.mBooted) {
+ if (mService.mAm.mBooting || !mService.mAm.mBooted) {
if (r != null && r.idle) {
checkFinishBootingLocked();
}
@@ -760,7 +760,7 @@
}
boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
- if (!mService.mBooting && !mService.mBooted) {
+ if (!mService.mAm.mBooting && !mService.mAm.mBooted) {
// Not ready yet!
return false;
}
@@ -774,7 +774,7 @@
moveFocusableActivityStackToFrontLocked(r, myReason);
return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
}
- return mService.startHomeActivityLocked(mCurrentUser, myReason);
+ return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason);
}
TaskRecord anyTaskForIdLocked(int id) {
@@ -912,7 +912,7 @@
// result to an activity belonging to userId. Example case: a document
// picker for personal files, opened by a work app, should still get locked.
if (taskTopActivityIsUser(task, userId)) {
- mService.mTaskChangeNotificationController.notifyTaskProfileLocked(
+ mService.mAm.mActivityTaskManager.getTaskChangeNotificationController().notifyTaskProfileLocked(
task.taskId, userId);
}
}
@@ -1167,7 +1167,7 @@
}
}
if (changed) {
- mService.notifyAll();
+ mService.mAm.notifyAll();
}
}
@@ -1197,7 +1197,7 @@
}
if (changed) {
- mService.notifyAll();
+ mService.mAm.notifyAll();
}
}
@@ -1218,7 +1218,7 @@
}
}
if (changed) {
- mService.notifyAll();
+ mService.mAm.notifyAll();
}
}
@@ -1323,19 +1323,19 @@
// Don't debug things in the system process
if (!aInfo.processName.equals("system")) {
if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
- mService.setDebugApp(aInfo.processName, true, false);
+ mService.mAm.setDebugApp(aInfo.processName, true, false);
}
if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
- mService.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+ mService.mAm.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
}
if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
- mService.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
+ mService.mAm.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
}
if (profilerInfo != null) {
- mService.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+ mService.mAm.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
}
}
final String intentLaunchToken = intent.getLaunchToken();
@@ -1348,7 +1348,7 @@
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
try {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
int modifiedFlags = flags
@@ -1365,7 +1365,7 @@
// (e.g. AMS.startActivityAsUser).
final long token = Binder.clearCallingIdentity();
try {
- return mService.getPackageManagerInternalLocked().resolveIntent(
+ return mService.mAm.getPackageManagerInternalLocked().resolveIntent(
intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1401,12 +1401,13 @@
beginDeferResume();
try {
- r.startFreezingScreenLocked(app, 0);
+ final WindowProcessController proc = app.getWindowProcessController();
+ r.startFreezingScreenLocked(proc, 0);
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
- r.setProcess(app);
+ r.setProcess(proc);
if (getKeyguardController().isKeyguardLocked()) {
r.notifyUnknownVisibilityLaunched();
@@ -1450,12 +1451,8 @@
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- mService.updateLruProcessLocked(app, true, null);
- mService.updateOomAdjLocked();
+ proc.addActivityIfNeeded(r);
+ proc.updateProcessInfo(false, true, true, true);
final LockTaskController lockTaskController = mService.getLockTaskController();
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
@@ -1487,18 +1484,18 @@
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
- mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+ mService.mAm.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
r.sleeping = false;
r.forceNewConfig = false;
- mService.getAppWarningsLocked().onStartActivity(r);
- mService.showAskCompatModeDialogLocked(r);
- r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ mService.mAm.getAppWarningsLocked().onStartActivity(r);
+ mService.mAm.showAskCompatModeDialogLocked(r);
+ r.compat = mService.mAm.compatibilityInfoForPackageLocked(r.info.applicationInfo);
ProfilerInfo profilerInfo = null;
- if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
- if (mService.mProfileProc == null || mService.mProfileProc == app) {
- mService.mProfileProc = app;
- ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
+ if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
+ if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
+ mService.mAm.mProfileProc = app;
+ ProfilerInfo profilerInfoSvc = mService.mAm.mProfilerInfo;
if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
if (profilerInfoSvc.profileFd != null) {
try {
@@ -1515,13 +1512,13 @@
app.hasShownUi = true;
app.pendingUiClean = true;
- app.forceProcessStateUpTo(mService.mTopProcessState);
+ app.forceProcessStateUpTo(mService.mAm.mTopProcessState);
// Because we could be starting an Activity in the system process this may not go
// across a Binder interface which would create a new Configuration. Consequently
// we have to always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
+ mService.mAm.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
@@ -1536,9 +1533,9 @@
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
- r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
- r.persistentState, results, newIntents, mService.isNextTransitionForward(),
- profilerInfo));
+ r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+ r.icicle, r.persistentState, results, newIntents,
+ mService.isNextTransitionForward(), profilerInfo));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -1552,25 +1549,24 @@
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
-
if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
- && mService.mHasHeavyWeightFeature) {
+ && mService.mAm.mHasHeavyWeightFeature) {
// This may be a heavy-weight process! Note that the package
// manager will ensure that only activity can run in the main
// process of the .apk, which is the only thing that will be
// considered heavy-weight.
if (app.processName.equals(app.info.packageName)) {
- if (mService.mHeavyWeightProcess != null
- && mService.mHeavyWeightProcess != app) {
+ if (mService.mAm.mHeavyWeightProcess != null
+ && mService.mAm.mHeavyWeightProcess != app) {
Slog.w(TAG, "Starting new heavy weight process " + app
+ " when already running "
- + mService.mHeavyWeightProcess);
+ + mService.mAm.mHeavyWeightProcess);
}
- mService.mHeavyWeightProcess = app;
- Message msg = mService.mHandler.obtainMessage(
+ mService.mAm.mHeavyWeightProcess = app;
+ Message msg = mService.mAm.mHandler.obtainMessage(
ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
msg.obj = r;
- mService.mHandler.sendMessage(msg);
+ mService.mAm.mHandler.sendMessage(msg);
}
}
@@ -1581,7 +1577,7 @@
Slog.e(TAG, "Second failure launching "
+ r.intent.getComponent().flattenToShortString()
+ ", giving up", e);
- mService.appDiedLocked(app);
+ mService.mAm.appDiedLocked(app);
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"2nd-crash", false);
return false;
@@ -1590,7 +1586,7 @@
// This is the first time we failed -- restart process and
// retry.
r.launchFailed = true;
- app.activities.remove(r);
+ proc.removeActivity(r);
throw e;
}
} finally {
@@ -1628,7 +1624,7 @@
// Update any services we are bound to that might care about whether
// their client may have activities.
if (r.app != null) {
- mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
+ r.app.updateServiceConnectionActivities();
}
return true;
@@ -1642,6 +1638,8 @@
* @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
* {@code true} if config changed.
* @param deferResume Whether to defer resume while updating config.
+ * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+ * because of configuration update.
*/
boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
boolean markFrozenIfConfigChanged, boolean deferResume) {
@@ -1652,6 +1650,11 @@
ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */, false /* notifyClients */);
+ if (displayId == INVALID_DISPLAY) {
+ // The caller didn't provide a valid display id, skip updating config.
+ return true;
+ }
+
// Force-update the orientation from the WindowManager, since we need the true configuration
// to send to the client now.
final Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -1686,7 +1689,7 @@
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
- ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+ ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
getLaunchTimeTracker().setLaunchTime(r);
@@ -1700,7 +1703,7 @@
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
- mService.mProcessStats);
+ mService.mAm.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
@@ -1713,7 +1716,7 @@
// restart the application.
}
- mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+ mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
@@ -1729,16 +1732,16 @@
|| !resumedActivity.app.equals(targetActivity.app);
}
- if (sendHint && mService.mLocalPowerManager != null) {
- mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
+ if (sendHint && mService.mAm.mLocalPowerManager != null) {
+ mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
mPowerHintSent = true;
}
}
void sendPowerHintForLaunchEndIfNeeded() {
// Trigger launch power hint if activity is launched
- if (mPowerHintSent && mService.mLocalPowerManager != null) {
- mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
+ if (mPowerHintSent && mService.mAm.mLocalPowerManager != null) {
+ mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
mPowerHintSent = false;
}
}
@@ -1747,9 +1750,9 @@
String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask,
ProcessRecord callerApp, ActivityRecord resultRecord, ActivityStack resultStack) {
- final boolean isCallerRecents = mService.getRecentTasks() != null &&
- mService.getRecentTasks().isCallerRecents(callingUid);
- final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
+ final boolean isCallerRecents = mService.getRecentTasks() != null
+ && mService.getRecentTasks().isCallerRecents(callingUid);
+ final int startAnyPerm = mService.mAm.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
// If the caller has START_ANY_ACTIVITY, ignore all checks below. In addition, if the
@@ -1828,7 +1831,7 @@
// Check if the caller has enough privileges to embed activities and launch to private
// displays.
- final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
+ final int startAnyPerm = mService.mAm.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -1850,7 +1853,7 @@
return false;
}
// Check if the caller is allowed to embed activities from other apps.
- if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
+ if (mService.mAm.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
== PERMISSION_DENIED && !uidPresentOnDisplay) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ " disallow activity embedding without permission.");
@@ -1908,7 +1911,7 @@
private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
- if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
+ if (!ignoreTargetSecurity && mService.mAm.checkComponentPermission(activityInfo.permission,
callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
== PERMISSION_DENIED) {
return ACTIVITY_RESTRICTION_PERMISSION;
@@ -1923,7 +1926,7 @@
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.mAppOpsService.noteOperation(opCode, callingUid,
+ if (mService.mAm.mAppOpsService.noteOperation(opCode, callingUid,
callingPackage) != AppOpsManager.MODE_ALLOWED) {
if (!ignoreTargetSecurity) {
return ACTIVITY_RESTRICTION_APPOP;
@@ -1957,7 +1960,7 @@
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
+ if (mService.mAm.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
return ACTIVITY_RESTRICTION_PERMISSION;
}
@@ -1966,7 +1969,7 @@
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.mAppOpsService.noteOperation(opCode, callingUid,
+ if (mService.mAm.mAppOpsService.noteOperation(opCode, callingUid,
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return ACTIVITY_RESTRICTION_APPOP;
}
@@ -1991,19 +1994,19 @@
/**
* Called when the frontmost task is idle.
- * @return the state of mService.mBooting before this was called.
+ * @return the state of mService.mAm.mBooting before this was called.
*/
@GuardedBy("mService")
private boolean checkFinishBootingLocked() {
- final boolean booting = mService.mBooting;
+ final boolean booting = mService.mAm.mBooting;
boolean enableScreen = false;
- mService.mBooting = false;
- if (!mService.mBooted) {
- mService.mBooted = true;
+ mService.mAm.mBooting = false;
+ if (!mService.mAm.mBooted) {
+ mService.mAm.mBooted = true;
enableScreen = true;
}
if (booting || enableScreen) {
- mService.postFinishBooting(booting, enableScreen);
+ mService.mAm.postFinishBooting(booting, enableScreen);
}
return booting;
}
@@ -2052,7 +2055,7 @@
if (allResumedActivitiesIdle()) {
if (r != null) {
- mService.scheduleAppGcsLocked();
+ mService.mAm.scheduleAppGcsLocked();
}
if (mLaunchingActivity.isHeld()) {
@@ -2109,12 +2112,12 @@
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
- mService.mUserController.finishUserSwitch(startingUsers.get(i));
+ mService.mAm.mUserController.finishUserSwitch(startingUsers.get(i));
}
}
}
- mService.trimApplications();
+ mService.mAm.trimApplications();
//dump();
//mWindowManager.dump();
@@ -2125,7 +2128,7 @@
return r;
}
- boolean handleAppDiedLocked(ProcessRecord app) {
+ boolean handleAppDiedLocked(WindowProcessController app) {
boolean hasVisibleActivities = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
@@ -2188,7 +2191,7 @@
// First, found out what is currently the foreground app, so that
// we don't blow away the previous app if this activity is being
// hosted by the process that is actually still the foreground.
- ProcessRecord fgApp = null;
+ WindowProcessController fgApp = null;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -2207,7 +2210,7 @@
// Now set this one as the previous process, only if that really
// makes sense to.
- if (r.app != null && fgApp != null && r.app != fgApp
+ if (r.hasProcess() && fgApp != null && r.app != fgApp
&& r.lastVisibleTime > mService.mPreviousProcessVisibleTime
&& r.app != mService.mHomeProcess) {
mService.mPreviousProcess = r.app;
@@ -2257,7 +2260,7 @@
* @param reason Reason to perform this action.
* @return The task that was finished in this stack, {@code null} if haven't found any.
*/
- TaskRecord finishTopCrashedActivitiesLocked(ProcessRecord app, String reason) {
+ TaskRecord finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
TaskRecord finishedTask = null;
ActivityStack focusedStack = getFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3099,7 +3102,7 @@
}
// Find any running services associated with this app and stop if needed.
- mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+ mService.mAm.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
if (!killProcess) {
return;
@@ -3107,14 +3110,15 @@
// Determine if the process(es) for this task should be killed.
final String pkg = component.getPackageName();
- ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
- ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+ ArrayList<Object> procsToKill = new ArrayList<>();
+ ArrayMap<String, SparseArray<WindowProcessController>> pmap =
+ mService.mProcessNames.getMap();
for (int i = 0; i < pmap.size(); i++) {
- SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+ SparseArray<WindowProcessController> uids = pmap.valueAt(i);
for (int j = 0; j < uids.size(); j++) {
- ProcessRecord proc = uids.valueAt(j);
- if (proc.userId != tr.userId) {
+ WindowProcessController proc = uids.valueAt(j);
+ if (proc.mUserId != tr.userId) {
// Don't kill process for a different user.
continue;
}
@@ -3122,21 +3126,18 @@
// Don't kill the home process along with tasks from the same package.
continue;
}
- if (!proc.pkgList.containsKey(pkg)) {
+ if (!proc.mPkgList.contains(pkg)) {
// Don't kill process that is not associated with this task.
continue;
}
- for (int k = 0; k < proc.activities.size(); k++) {
- TaskRecord otherTask = proc.activities.get(k).getTask();
- if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
- // Don't kill process(es) that has an activity in a different task that is
- // also in recents.
- return;
- }
+ if (!proc.shouldKillProcessForRemovedTask(tr)) {
+ // Don't kill process(es) that has an activity in a different task that is also
+ // in recents.
+ return;
}
- if (proc.foregroundServices) {
+ if (proc.hasForegroundServices()) {
// Don't kill process(es) with foreground service.
return;
}
@@ -3146,17 +3147,12 @@
}
}
- // Kill the running processes.
- for (int i = 0; i < procsToKill.size(); i++) {
- ProcessRecord pr = procsToKill.get(i);
- if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- && pr.curReceivers.isEmpty()) {
- pr.kill("remove task", true);
- } else {
- // We delay killing processes that are not in the background or running a receiver.
- pr.waitingToKill = "remove task";
- }
- }
+ // Kill the running processes. Post on handle since we don't want to hold the service lock
+ // while calling into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
+ procsToKill);
+ mService.mH.post(r);
}
/**
@@ -3389,7 +3385,7 @@
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
- mService.mTaskChangeNotificationController.notifyActivityPinned(r);
+ mService.getTaskChangeNotificationController().notifyActivityPinned(r);
}
/** Move activity with its stack to front and make the stack focused. */
@@ -3496,7 +3492,7 @@
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
- mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ mService.mAm.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
}
}
@@ -3521,7 +3517,7 @@
long timeRemaining = endTime - System.currentTimeMillis();
if (timeRemaining > 0) {
try {
- mService.wait(timeRemaining);
+ mService.mAm.wait(timeRemaining);
} catch (InterruptedException e) {
}
} else {
@@ -3603,7 +3599,7 @@
}
void checkReadyForSleepLocked(boolean allowDelay) {
- if (!mService.isSleepingOrShuttingDownLocked()) {
+ if (!mService.mAm.isSleepingOrShuttingDownLocked()) {
// Do not care.
return;
}
@@ -3620,8 +3616,8 @@
if (mGoingToSleep.isHeld()) {
mGoingToSleep.release();
}
- if (mService.mShuttingDown) {
- mService.notifyAll();
+ if (mService.mAm.mShuttingDown) {
+ mService.mAm.notifyAll();
}
}
@@ -3649,7 +3645,7 @@
final ActivityStack stack = r.getStack();
if (isFocusedStack(stack)) {
- mService.updateUsageStats(r, true);
+ mService.mAm.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -3659,7 +3655,7 @@
return false;
}
- void handleAppCrashLocked(ProcessRecord app) {
+ void handleAppCrashLocked(WindowProcessController app) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -3676,7 +3672,7 @@
r.mLaunchTaskBehind = false;
mRecentTasks.add(task);
- mService.mTaskChangeNotificationController.notifyTaskStackChanged();
+ mService.getTaskChangeNotificationController().notifyTaskStackChanged();
r.setVisibility(false);
// When launching tasks behind, update the last active time of the top task after the new
@@ -3761,7 +3757,7 @@
}
}
- void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
+ void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -3771,44 +3767,9 @@
}
}
- void releaseSomeActivitiesLocked(ProcessRecord app, String reason) {
- // Examine all activities currently running in the process.
- TaskRecord firstTask = null;
+ void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
// Tasks is non-null only if two or more tasks are found.
- ArraySet<TaskRecord> tasks = null;
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
- for (int i = 0; i < app.activities.size(); i++) {
- ActivityRecord r = app.activities.get(i);
- // First, if we find an activity that is in the process of being destroyed,
- // then we just aren't going to do anything for now; we want things to settle
- // down before we try to prune more activities.
- if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
- return;
- }
- // Don't consider any activies that are currently not in a state where they
- // can be destroyed.
- if (r.visible || !r.stopped || !r.haveState
- || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
- continue;
- }
-
- final TaskRecord task = r.getTask();
- if (task != null) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
- + " from " + r);
- if (firstTask == null) {
- firstTask = task;
- } else if (firstTask != task) {
- if (tasks == null) {
- tasks = new ArraySet<>();
- tasks.add(firstTask);
- }
- tasks.add(task);
- }
- }
- }
+ ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
if (tasks == null) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
return;
@@ -3876,7 +3837,7 @@
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
- return mService.mUserController.isCurrentProfile(userId);
+ return mService.mAm.mUserController.isCurrentProfile(userId);
}
/**
@@ -3923,7 +3884,7 @@
final ActivityStack stack = s.getStack();
final boolean shouldSleepOrShutDown = stack != null
? stack.shouldSleepOrShutDownActivities()
- : mService.isSleepingOrShuttingDownLocked();
+ : mService.mAm.isSleepingOrShuttingDownLocked();
if (!waitingVisible || shouldSleepOrShutDown) {
if (!processPausingActivities && s.isState(PAUSING)) {
// Defer processing pausing activities in this iteration and reschedule
@@ -4201,16 +4162,16 @@
pw.print(innerPrefix); pw.println(r.app);
}
}
- if (client && r.app != null && r.app.thread != null) {
+ if (client && r.attachedToProcess()) {
// flush anything that is already in the PrintWriter since the thread is going
// to write to the file descriptor directly
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpActivity(tp.getWriteFd(), r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can
- // deadlock with the application.
+ r.app.getThread().dumpActivity(
+ tp.getWriteFd(), r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can deadlock with the application.
tp.go(fd, 2000);
} finally {
tp.kill();
@@ -4277,7 +4238,7 @@
}
private void handleDisplayAdded(int displayId) {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
getActivityDisplayOrCreateLocked(displayId);
}
}
@@ -4340,7 +4301,7 @@
throw new IllegalArgumentException("Can't remove the primary display.");
}
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
return;
@@ -4355,7 +4316,7 @@
}
private void handleDisplayChanged(int displayId) {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
// TODO: The following code block should be moved into {@link ActivityDisplay}.
if (activityDisplay != null) {
@@ -4364,7 +4325,7 @@
int displayState = activityDisplay.mDisplay.getState();
if (displayState == Display.STATE_OFF && activityDisplay.mOffToken == null) {
activityDisplay.mOffToken =
- mService.acquireSleepToken("Display-off", displayId);
+ mService.mAm.acquireSleepToken("Display-off", displayId);
} else if (displayState == Display.STATE_ON
&& activityDisplay.mOffToken != null) {
activityDisplay.mOffToken.release();
@@ -4397,7 +4358,7 @@
if (display != null) {
display.mAllSleepTokens.remove(token);
if (display.mAllSleepTokens.isEmpty()) {
- mService.updateSleepIfNeededLocked();
+ mService.mAm.updateSleepIfNeededLocked();
}
}
}
@@ -4411,7 +4372,7 @@
}
display.mAllSleepTokens.clear();
- mService.updateSleepIfNeededLocked();
+ mService.mAm.updateSleepIfNeededLocked();
}
private StackInfo getStackInfo(ActivityStack stack) {
@@ -4504,12 +4465,12 @@
}
// The task might have landed on a display different from requested.
// TODO(multi-display): Find proper stack for the task on the default display.
- mAtm.setTaskWindowingMode(task.taskId,
+ mService.setTaskWindowingMode(task.taskId,
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
if (preferredDisplayId != actualDisplayId) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
- mService.mTaskChangeNotificationController
+ mService.getTaskChangeNotificationController()
.notifyActivityLaunchOnSecondaryDisplayFailed();
return;
}
@@ -4518,7 +4479,7 @@
if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
// Display a warning toast that we tried to put an app that doesn't support split-screen
// in split-screen.
- mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack();
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
@@ -4538,7 +4499,7 @@
final int reason = isSecondaryDisplayPreferred
? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
: FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
- mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+ mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
task.taskId, reason, packageName);
}
}
@@ -4570,7 +4531,7 @@
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
mMultiWindowModeChangedActivities.add(r);
}
}
@@ -4593,7 +4554,7 @@
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
mPipModeChangedActivities.add(r);
// If we are scheduling pip change, then remove this activity from multi-window
// change list as the processing of pip change will make sure multi-window changed
@@ -4612,7 +4573,7 @@
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
}
@@ -4662,7 +4623,7 @@
}
void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
processPausingActivities, null);
}
@@ -4672,7 +4633,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
r.updateMultiWindowMode();
@@ -4680,7 +4641,7 @@
}
} break;
case REPORT_PIP_MODE_CHANGED_MSG: {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPipModeChangedActivities.remove(i);
r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
@@ -4702,20 +4663,20 @@
false /* processPausingActivities */);
} break;
case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
resumeFocusedStackTopActivityLocked();
}
} break;
case SLEEP_TIMEOUT_MSG: {
- synchronized (mService) {
- if (mService.isSleepingOrShuttingDownLocked()) {
+ synchronized (mService.mGlobalLock) {
+ if (mService.mAm.isSleepingOrShuttingDownLocked()) {
Slog.w(TAG, "Sleep timeout! Sleeping now.");
checkReadyForSleepLocked(false /* allowDelay */);
}
}
} break;
case LAUNCH_TIMEOUT_MSG: {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (mLaunchingActivity.isHeld()) {
Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
if (VALIDATE_WAKE_LOCK_CALLER
@@ -4736,7 +4697,7 @@
handleDisplayRemoved(msg.arg1);
} break;
case LAUNCH_TASK_BEHIND_COMPLETE: {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
if (r != null) {
handleLaunchTaskBehindCompleteLocked(r);
@@ -4824,14 +4785,14 @@
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
- if (!mService.mUserController.shouldConfirmCredentials(task.userId)
+ if (!mService.mAm.mUserController.shouldConfirmCredentials(task.userId)
&& task.getRootActivity() != null) {
final ActivityRecord targetActivity = task.getTopActivity();
sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
mActivityMetricsLogger.notifyActivityLaunching();
try {
- mService.mActivityTaskManager.moveTaskToFrontLocked(task.taskId, 0, options,
+ mService.moveTaskToFrontLocked(task.taskId, 0, options,
true /* fromRecents */);
} finally {
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
@@ -4952,7 +4913,7 @@
@Override
public void release() {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
removeSleepTokenLocked(this);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index a7c3200..f7ea4b2 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -21,7 +21,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import android.app.IApplicationThread;
import android.content.ComponentName;
@@ -65,7 +65,7 @@
private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
/** Last home activity record we attempted to start. */
@@ -96,7 +96,7 @@
public void handleMessage(Message msg) {
switch(msg.what) {
case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
doPendingActivityLaunches(true);
}
break;
@@ -111,22 +111,22 @@
*/
private ActivityStarter mLastStarter;
- ActivityStartController(ActivityManagerService service) {
+ ActivityStartController(ActivityTaskManagerService service) {
this(service, service.mStackSupervisor,
new DefaultFactory(service, service.mStackSupervisor,
new ActivityStartInterceptor(service, service.mStackSupervisor)));
}
@VisibleForTesting
- ActivityStartController(ActivityManagerService service, ActivityStackSupervisor supervisor,
+ ActivityStartController(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
Factory factory) {
mService = service;
mSupervisor = supervisor;
- mHandler = new StartHandler(mService.mHandlerThread.getLooper());
+ mHandler = new StartHandler(mService.mH.getLooper());
mFactory = factory;
mFactory.setController(this);
mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
- service.mHandler);
+ service.mH);
}
/**
@@ -182,7 +182,7 @@
*/
void startSetupActivity() {
// Only do this once per boot.
- if (mService.getCheckedForSetup()) {
+ if (mService.mAm.getCheckedForSetup()) {
return;
}
@@ -190,10 +190,10 @@
// version than the last one shown, and we are not running in
// low-level factory test mode.
final ContentResolver resolver = mService.mContext.getContentResolver();
- if (mService.mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL &&
+ if (mService.mAm.mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL &&
Settings.Global.getInt(resolver,
Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
- mService.setCheckedForSetup(true);
+ mService.mAm.setCheckedForSetup(true);
// See if we should be showing the platform update setup UI.
final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
@@ -237,10 +237,10 @@
int checkTargetUser(int targetUserId, boolean validateIncomingUser,
int realCallingPid, int realCallingUid, String reason) {
if (validateIncomingUser) {
- return mService.mUserController.handleIncomingUser(realCallingPid, realCallingUid,
- targetUserId, false, ALLOW_FULL_ONLY, reason, null);
+ return mService.handleIncomingUser(
+ realCallingPid, realCallingUid, targetUserId, reason);
} else {
- mService.mUserController.ensureNotSpecialUser(targetUserId);
+ mService.mAm.mUserController.ensureNotSpecialUser(targetUserId);
return targetUserId;
}
}
@@ -320,7 +320,7 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
ActivityRecord[] outActivity = new ActivityRecord[1];
for (int i=0; i < intents.length; i++) {
Intent intent = intents[i];
@@ -343,7 +343,7 @@
null, userId, ActivityStarter.computeResolveFilterUid(
callingUid, realCallingUid, UserHandle.USER_NULL));
// TODO: New, check if this is correct
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
+ aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
if (aInfo != null &&
(aInfo.applicationInfo.privateFlags
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index ff97db8..171c0bb 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -66,7 +66,7 @@
*/
class ActivityStartInterceptor {
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private final Context mServiceContext;
private final UserController mUserController;
@@ -99,12 +99,13 @@
TaskRecord mInTask;
ActivityOptions mActivityOptions;
- ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
- this(service, supervisor, service.mContext, service.mUserController);
+ ActivityStartInterceptor(
+ ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
+ this(service, supervisor, service.mContext, service.mAm.mUserController);
}
@VisibleForTesting
- ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor,
+ ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
Context context, UserController userController) {
mService = service;
mSupervisor = supervisor;
@@ -127,7 +128,7 @@
private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
- final IIntentSender target = mService.getIntentSenderLocked(
+ final IIntentSender target = mService.mAm.getIntentSenderLocked(
INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
null /*resultCode*/, 0 /*requestCode*/,
new Intent[] { mIntent }, new String[] { mResolvedType },
@@ -238,7 +239,7 @@
(mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
return false;
}
- final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
+ final PackageManagerInternal pmi = mService.mAm.getPackageManagerInternalLocked();
if (pmi == null) {
return false;
}
@@ -319,7 +320,7 @@
private boolean interceptHarmfulAppIfNeeded() {
CharSequence harmfulAppWarning;
try {
- harmfulAppWarning = mService.getPackageManager()
+ harmfulAppWarning = mService.mAm.getPackageManager()
.getHarmfulAppWarning(mAInfo.packageName, mUserId);
} catch (RemoteException ex) {
return false;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3297560..dac7715 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -132,7 +132,7 @@
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
private static final int INVALID_LAUNCH_MODE = -1;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private final ActivityStartInterceptor mInterceptor;
private final ActivityStartController mController;
@@ -233,14 +233,14 @@
private final int MAX_STARTER_COUNT = 3;
private ActivityStartController mController;
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityStartInterceptor mInterceptor;
private SynchronizedPool<ActivityStarter> mStarterPool =
new SynchronizedPool<>(MAX_STARTER_COUNT);
- DefaultFactory(ActivityManagerService service,
+ DefaultFactory(ActivityTaskManagerService service,
ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
mService = service;
mSupervisor = supervisor;
@@ -410,7 +410,7 @@
}
}
- ActivityStarter(ActivityStartController controller, ActivityManagerService service,
+ ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
mController = controller;
mService = service;
@@ -583,7 +583,7 @@
ProcessRecord callerApp = null;
if (caller != null) {
- callerApp = mService.getRecordForAppLocked(caller);
+ callerApp = mService.mAm.getRecordForAppLocked(caller);
if (callerApp != null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;
@@ -672,7 +672,7 @@
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
try {
intent.addCategory(Intent.CATEGORY_VOICE);
- if (!mService.getPackageManager().activitySupportsIntent(
+ if (!mService.mAm.getPackageManager().activitySupportsIntent(
intent.getComponent(), intent, resolvedType)) {
Slog.w(TAG,
"Activity being started in current voice task does not support voice: "
@@ -690,7 +690,7 @@
// If the caller is starting a new voice session, just make sure the target
// is actually allowing it to run this way.
try {
- if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),
+ if (!mService.mAm.getPackageManager().activitySupportsIntent(intent.getComponent(),
intent, resolvedType)) {
Slog.w(TAG,
"Activity being started in new voice task does not support: "
@@ -717,13 +717,14 @@
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
inTask != null, callerApp, resultRecord, resultStack);
- abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
+ abort |= !mService.mAm.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
+ final WindowProcessController callerWpc =
+ callerApp != null ? callerApp.getWindowProcessController() : null;
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
- ? options.getOptions(intent, aInfo, callerApp, mSupervisor)
- : null;
+ ? options.getOptions(intent, aInfo, callerWpc, mSupervisor) : null;
if (allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
@@ -770,10 +771,10 @@
// If permissions need a review before any of the app components can run, we
// launch the review activity and pass a pending intent to start the activity
// we are to launching now after the review is completed.
- if (mService.mPermissionReviewRequired && aInfo != null) {
- if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ if (mService.mAm.mPermissionReviewRequired && aInfo != null) {
+ if (mService.mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
aInfo.packageName, userId)) {
- IIntentSender target = mService.getIntentSenderLocked(
+ IIntentSender target = mService.mAm.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
callingUid, userId, null, null, 0, new Intent[]{intent},
new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -823,8 +824,9 @@
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
}
- ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
- callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
+ ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid,
+ callingUid,
+ callingPackage, intent, resolvedType, aInfo, mService.mAm.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, checkedOptions, sourceRecord);
if (outActivity != null) {
@@ -846,23 +848,13 @@
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "Activity start")) {
mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, stack, callerApp));
+ sourceRecord, startFlags, stack, callerWpc));
ActivityOptions.abort(checkedOptions);
return ActivityManager.START_SWITCHES_CANCELED;
}
}
- if (mService.mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches,
- // so now just generally allow switches. Use case: user presses
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mService.mAppSwitchesAllowedTime = 0;
- } else {
- mService.mDidAppSwitch = true;
- }
-
+ mService.onStartActivitySetDidAppSwitch();
mController.doPendingActivityLaunches(false);
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
@@ -878,7 +870,7 @@
String resolvedType, int userId) {
if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
// request phase two resolution
- mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
+ mService.mAm.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
auxiliaryResponse, originalIntent, resolvedType, callingPackage,
verificationBundle, userId);
}
@@ -929,7 +921,7 @@
// anyone interested in this piece of information.
switch (startedActivityStack.getWindowingMode()) {
case WINDOWING_MODE_PINNED:
- mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+ mService.getTaskChangeNotificationController().notifyPinnedActivityRestartAttempt(
clearedTask);
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
@@ -978,7 +970,7 @@
&& !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
&& !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
- && mService.getPackageManagerInternalLocked()
+ && mService.mAm.getPackageManagerInternalLocked()
.isInstantAppInstallerComponent(intent.getComponent())) {
// intercept intents targeted directly to the ephemeral installer the
// ephemeral installer should never be started with a raw Intent; instead
@@ -1020,10 +1012,10 @@
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
final ActivityStack stack = mSupervisor.mFocusedStack;
stack.mConfigWillChange = globalConfig != null
- && mService.getGlobalConfiguration().diff(globalConfig) != 0;
+ && mService.mAm.getGlobalConfiguration().diff(globalConfig) != 0;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Starting activity when config will change = " + stack.mConfigWillChange);
@@ -1032,16 +1024,16 @@
if (aInfo != null &&
(aInfo.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
- mService.mHasHeavyWeightFeature) {
+ mService.mAm.mHasHeavyWeightFeature) {
// This may be a heavy-weight process! Check to see if we already
// have another, different heavy-weight process running.
if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
- final ProcessRecord heavy = mService.mHeavyWeightProcess;
+ final ProcessRecord heavy = mService.mAm.mHeavyWeightProcess;
if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
|| !heavy.processName.equals(aInfo.processName))) {
int appCallingUid = callingUid;
if (caller != null) {
- ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+ ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller);
if (callerApp != null) {
appCallingUid = callerApp.info.uid;
} else {
@@ -1053,7 +1045,7 @@
}
}
- IIntentSender target = mService.getIntentSenderLocked(
+ IIntentSender target = mService.mAm.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, "android",
appCallingUid, userId, null, null, 0, new Intent[] { intent },
new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
@@ -1066,13 +1058,8 @@
}
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
new IntentSender(target));
- if (heavy.activities.size() > 0) {
- ActivityRecord hist = heavy.activities.get(0);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
- hist.packageName);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
- hist.getTask().taskId);
- }
+ heavy.getWindowProcessController().updateIntentForHeavyWeightActivity(
+ newIntent);
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
aInfo.packageName);
newIntent.setFlags(intent.getFlags());
@@ -1089,7 +1076,7 @@
callingUid, realCallingUid, mRequest.filterCallingUid));
aInfo = rInfo != null ? rInfo.activityInfo : null;
if (aInfo != null) {
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
+ aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
}
}
}
@@ -1109,7 +1096,7 @@
// do so now. This allows a clean switch, as we are waiting
// for the current activity to pause (so we will not destroy
// it), and have not yet started the next activity.
- mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
stack.mConfigWillChange = false;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1127,7 +1114,7 @@
mSupervisor.mWaitingActivityLaunched.add(outResult);
do {
try {
- mService.wait();
+ mService.mGlobalLock.wait();
} catch (InterruptedException e) {
}
} while (outResult.result != START_TASK_TO_FRONT
@@ -1158,7 +1145,7 @@
// Note: the timeout variable is not currently not ever set.
do {
try {
- mService.wait();
+ mService.mGlobalLock.wait();
} catch (InterruptedException e) {
}
} while (!outResult.timeout && outResult.who == null);
@@ -1373,7 +1360,7 @@
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
&& top.userId == mStartActivity.userId
- && top.app != null && top.app.thread != null
+ && top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
if (dontStart) {
@@ -1422,9 +1409,9 @@
return result;
}
- mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
+ mService.mAm.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
- mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
+ mService.mAm.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
@@ -2114,13 +2101,13 @@
// be not suitable. Let's check other displays.
if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
// Can't use target display, lets find a stack on the source display.
- mTargetStack = mService.mStackSupervisor.getValidLaunchStackOnDisplay(
+ mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
sourceStack.mDisplayId, mStartActivity);
}
if (mTargetStack == null) {
// There are no suitable stacks on the target and source display(s). Look on all
// displays.
- mTargetStack = mService.mStackSupervisor.getNextValidLaunchStackLocked(
+ mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
mStartActivity, -1 /* currentFocus */);
}
}
@@ -2251,7 +2238,7 @@
final ActivityStack stack = task.getStack();
if (stack != null && stack.resizeStackWithLaunchBounds()) {
- mService.mActivityTaskManager.resizeStack(
+ mService.resizeStack(
stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
} else {
task.updateOverrideConfiguration(bounds);
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index c6edfe5..d47fb44 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -24,14 +24,17 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STOP_APP_SWITCHES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static com.android.server.am.ActivityManagerService.dumpStackTraces;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -42,19 +45,36 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
+import static android.os.Build.VERSION_CODES.N;
import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
@@ -63,11 +83,13 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityManagerService.DISPATCH_SCREEN_KEYGUARD_MSG;
-import static com.android.server.am.ActivityManagerService.ENTER_ANIMATION_COMPLETE_MSG;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG;
+import static com.android.server.am.ActivityManagerService.checkComponentPermission;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
@@ -83,13 +105,17 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
+import android.app.IActivityController;
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
@@ -102,56 +128,89 @@
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.servertransaction.ConfigurationChangeItem;
import android.app.usage.UsageEvents;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.LocaleList;
+import android.os.Looper;
+import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
-import android.os.TransactionTooLargeException;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.telecom.TelecomManager;
import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.StatsLog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.util.Preconditions;
+import com.android.server.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.WindowManagerService;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* System service for managing activities and their containers (task, stacks, displays,... ).
@@ -166,13 +225,31 @@
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+ private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
- private Context mContext;
- private ActivityManagerService mAm;
+ Context mContext;
+ H mH;
+ UiHandler mUiHandler;
+ ActivityManagerService mAm;
+ ActivityManagerInternal mAmInternal;
/* Global service lock used by the package the owns this service. */
Object mGlobalLock;
- private ActivityStackSupervisor mStackSupervisor;
+ ActivityStackSupervisor mStackSupervisor;
+ WindowManagerService mWindowManager;
+ /** All processes currently running that might have a window organized by name. */
+ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
+ /** This is the process holding what we currently consider to be the "home" activity. */
+ WindowProcessController mHomeProcess;
+ /**
+ * This is the process holding the activity the user last visited that is in a different process
+ * from the one they are currently in.
+ */
+ WindowProcessController mPreviousProcess;
+ /** The time at which the previous process was last visible. */
+ long mPreviousProcessVisibleTime;
+ /** List of intents that were used to start the most recent tasks. */
+ private RecentTasks mRecentTasks;
/** State of external calls telling us if the device is awake or asleep. */
private boolean mKeyguardShown = false;
@@ -196,6 +273,18 @@
private final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
+ // Keeps track of the active voice interaction service component, notified from
+ // VoiceInteractionManagerService
+ ComponentName mActiveVoiceInteractionServiceComponent;
+
+ private VrController mVrController;
+ KeyguardController mKeyguardController;
+ private final ClientLifecycleManager mLifecycleManager;
+ private TaskChangeNotificationController mTaskChangeNotificationController;
+ /** The controller for all operations related to locktask. */
+ private LockTaskController mLockTaskController;
+ private ActivityStartController mActivityStartController;
+
boolean mSuppressResizeConfigChanges;
private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
@@ -213,19 +302,228 @@
}
}
+ /** Current sequencing integer of the configuration, for skipping old configurations. */
+ private int mConfigurationSeq;
+ // To cache the list of supported system locales
+ private String[] mSupportedSystemLocales = null;
+
+ /**
+ * Temp object used when global and/or display override configuration is updated. It is also
+ * sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
+ * anyone...
+ */
+ private Configuration mTempConfig = new Configuration();
+
+ // Amount of time after a call to stopAppSwitches() during which we will
+ // prevent further untrusted switches from happening.
+ private static final long APP_SWITCH_DELAY_TIME = 5 * 1000;
+
+ /**
+ * The time at which we will allow normal application switches again,
+ * after a call to {@link #stopAppSwitches()}.
+ */
+ long mAppSwitchesAllowedTime;
+ /**
+ * This is set to true after the first switch after mAppSwitchesAllowedTime
+ * is set; any switches after that will clear the time.
+ */
+ boolean mDidAppSwitch;
+
+ IActivityController mController = null;
+ boolean mControllerIsAMonkey = false;
+
+ /**
+ * Used to retain an update lock when the foreground activity is in
+ * immersive mode.
+ */
+ final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
+ /**
+ * Packages that are being allowed to perform unrestricted app switches. Mapping is
+ * User -> Type -> uid.
+ */
+ final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
+
+ /** The dimensions of the thumbnails in the Recents UI. */
+ int mThumbnailWidth;
+ int mThumbnailHeight;
+ float mFullscreenThumbnailScale;
+
+ /**
+ * Flag that indicates if multi-window is enabled.
+ *
+ * For any particular form of multi-window to be enabled, generic multi-window must be enabled
+ * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
+ * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
+ * At least one of the forms of multi-window must be enabled in order for this flag to be
+ * initialized to 'true'.
+ *
+ * @see #mSupportsSplitScreenMultiWindow
+ * @see #mSupportsFreeformWindowManagement
+ * @see #mSupportsPictureInPicture
+ * @see #mSupportsMultiDisplay
+ */
+ boolean mSupportsMultiWindow;
+ boolean mSupportsSplitScreenMultiWindow;
+ boolean mSupportsFreeformWindowManagement;
+ boolean mSupportsPictureInPicture;
+ boolean mSupportsMultiDisplay;
+ boolean mForceResizableActivities;
+
+ final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
+
+ // VR Vr2d Display Id.
+ int mVr2dDisplayId = INVALID_DISPLAY;
+
ActivityTaskManagerService(Context context) {
mContext = context;
+ mLifecycleManager = new ClientLifecycleManager();
}
void onSystemReady() {
mAssistUtils = new AssistUtils(mContext);
+ mVrController.onSystemReady();
+ mRecentTasks.onSystemReadyLocked();
+ }
+
+ void retrieveSettings(ContentResolver resolver) {
+ final boolean freeformWindowManagement =
+ mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+ || Settings.Global.getInt(
+ resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+
+ final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
+ final boolean supportsPictureInPicture = supportsMultiWindow &&
+ mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ final boolean supportsSplitScreenMultiWindow =
+ ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
+ final boolean supportsMultiDisplay = mContext.getPackageManager()
+ .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+ final boolean alwaysFinishActivities =
+ Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+ final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
+ final boolean forceResizable = Settings.Global.getInt(
+ resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+
+ // Transfer any global setting for forcing RTL layout, into a System Property
+ SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+
+ final Configuration configuration = new Configuration();
+ Settings.System.getConfiguration(resolver, configuration);
+ if (forceRtl) {
+ // This will take care of setting the correct layout direction flags
+ configuration.setLayoutDirection(configuration.locale);
+ }
+
+ synchronized (mGlobalLock) {
+ mForceResizableActivities = forceResizable;
+ final boolean multiWindowFormEnabled = freeformWindowManagement
+ || supportsSplitScreenMultiWindow
+ || supportsPictureInPicture
+ || supportsMultiDisplay;
+ if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
+ mSupportsMultiWindow = true;
+ mSupportsFreeformWindowManagement = freeformWindowManagement;
+ mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+ mSupportsPictureInPicture = supportsPictureInPicture;
+ mSupportsMultiDisplay = supportsMultiDisplay;
+ } else {
+ mSupportsMultiWindow = false;
+ mSupportsFreeformWindowManagement = false;
+ mSupportsSplitScreenMultiWindow = false;
+ mSupportsPictureInPicture = false;
+ mSupportsMultiDisplay = false;
+ }
+ mWindowManager.setForceResizableTasks(mForceResizableActivities);
+ mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
+ // This happens before any activities are started, so we can change global configuration
+ // in-place.
+ updateConfigurationLocked(configuration, null, true);
+ final Configuration globalConfig = getGlobalConfiguration();
+ if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
+
+ // Load resources only after the current configuration has been set.
+ final Resources res = mContext.getResources();
+ mThumbnailWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.thumbnail_height);
+
+ if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+ mFullscreenThumbnailScale = (float) res
+ .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
+ (float) globalConfig.screenWidthDp;
+ } else {
+ mFullscreenThumbnailScale = res.getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+ }
+ }
}
// TODO: Will be converted to WM lock once transition is complete.
void setActivityManagerService(ActivityManagerService am) {
mAm = am;
mGlobalLock = mAm;
- mStackSupervisor = mAm.mStackSupervisor;
+ mH = new H(mAm.mHandlerThread.getLooper());
+ mUiHandler = new UiHandler();
+
+ mTempConfig.setToDefaults();
+ mTempConfig.setLocales(LocaleList.getDefault());
+ mConfigurationSeq = mTempConfig.seq = 1;
+ mStackSupervisor = createStackSupervisor();
+ mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+ mTaskChangeNotificationController =
+ new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
+ mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
+ mActivityStartController = new ActivityStartController(this);
+ mRecentTasks = createRecentTasks();
+ mStackSupervisor.setRecentTasks(mRecentTasks);
+ mVrController = new VrController(mGlobalLock);
+ mKeyguardController = mStackSupervisor.getKeyguardController();
+ }
+
+ void onActivityManagerInternalAdded() {
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ }
+
+ protected ActivityStackSupervisor createStackSupervisor() {
+ final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
+ supervisor.initialize();
+ return supervisor;
+ }
+
+ void setWindowManager(WindowManagerService wm) {
+ mWindowManager = wm;
+ mLockTaskController.setWindowManager(wm);
+ }
+
+ protected RecentTasks createRecentTasks() {
+ return new RecentTasks(this, mStackSupervisor);
+ }
+
+ RecentTasks getRecentTasks() {
+ return mRecentTasks;
+ }
+
+ ClientLifecycleManager getLifecycleManager() {
+ return mLifecycleManager;
+ }
+
+ ActivityStartController getActivityStartController() {
+ return mActivityStartController;
+ }
+
+ TaskChangeNotificationController getTaskChangeNotificationController() {
+ return mTaskChangeNotificationController;
+ }
+
+ LockTaskController getLockTaskController() {
+ return mLockTaskController;
+ }
+
+ private void start() {
+ LocalServices.addService(ActivityTaskManagerInternal.class, new LocalService());
}
public static final class Lifecycle extends SystemService {
@@ -239,6 +537,7 @@
@Override
public void onStart() {
publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
+ mService.start();
}
public ActivityTaskManagerService getService() {
@@ -260,11 +559,10 @@
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
int userId) {
final String reason = "startActivities";
- mAm.enforceNotIsolatedCaller(reason);
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, reason, null);
+ enforceNotIsolatedCaller(reason);
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
// TODO: Switch to user app stacks here.
- return mAm.getActivityStartController().startActivities(caller, -1, callingPackage, intents,
+ return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason);
}
@@ -281,13 +579,13 @@
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
boolean validateIncomingUser) {
- mAm.enforceNotIsolatedCaller("startActivityAsUser");
+ enforceNotIsolatedCaller("startActivityAsUser");
- userId = mAm.getActivityStartController().checkTargetUser(userId, validateIncomingUser,
+ userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
// TODO: Switch to user app stacks here.
- return mAm.getActivityStartController().obtainStarter(intent, "startActivityAsUser")
+ return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
@@ -305,9 +603,8 @@
@Override
public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions)
- throws TransactionTooLargeException {
- mAm.enforceNotIsolatedCaller("startActivityIntentSender");
+ String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
+ enforceNotIsolatedCaller("startActivityIntentSender");
// Refuse possible leaked file descriptors
if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -322,15 +619,14 @@
synchronized (mGlobalLock) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- final ActivityStack stack = mAm.getFocusedStack();
+ final ActivityStack stack = getFocusedStack();
if (stack.mResumedActivity != null &&
stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
- mAm.mAppSwitchesAllowedTime = 0;
+ mAppSwitchesAllowedTime = 0;
}
}
- int ret = pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+ return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
- return ret;
}
@Override
@@ -348,7 +644,7 @@
SafeActivityOptions.abort(options);
return false;
}
- if (r.app == null || r.app.thread == null) {
+ if (!r.attachedToProcess()) {
// The caller is not running... d'oh!
SafeActivityOptions.abort(options);
return false;
@@ -426,9 +722,9 @@
final long origId = Binder.clearCallingIdentity();
// TODO(b/64750076): Check if calling pid should really be -1.
- final int res = mAm.getActivityStartController()
+ final int res = getActivityStartController()
.obtainStarter(intent, "startNextMatchingActivity")
- .setCaller(r.app.thread)
+ .setCaller(r.app.getThread())
.setResolvedType(r.resolvedType)
.setActivityInfo(aInfo)
.setResultTo(resultTo != null ? resultTo.appToken : null)
@@ -457,12 +753,11 @@
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
final WaitResult res = new WaitResult();
synchronized (mGlobalLock) {
- mAm.enforceNotIsolatedCaller("startActivityAndWait");
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY,
- "startActivityAndWait", null);
+ enforceNotIsolatedCaller("startActivityAndWait");
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, "startActivityAndWait");
// TODO: Switch to user app stacks here.
- mAm.getActivityStartController().obtainStarter(intent, "startActivityAndWait")
+ getActivityStartController().obtainStarter(intent, "startActivityAndWait")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
@@ -484,12 +779,11 @@
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, Configuration config, Bundle bOptions, int userId) {
synchronized (mGlobalLock) {
- mAm.enforceNotIsolatedCaller("startActivityWithConfig");
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY,
- "startActivityWithConfig", null);
+ enforceNotIsolatedCaller("startActivityWithConfig");
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ "startActivityWithConfig");
// TODO: Switch to user app stacks here.
- return mAm.getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
+ return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
@@ -533,12 +827,12 @@
if (sourceRecord.app == null) {
throw new SecurityException("Called without a process attached to activity");
}
- if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
+ if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
// This is still okay, as long as this activity is running under the
// uid of the original calling activity.
- if (sourceRecord.app.uid != sourceRecord.launchedFromUid) {
+ if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
throw new SecurityException(
- "Calling activity in uid " + sourceRecord.app.uid
+ "Calling activity in uid " + sourceRecord.app.mUid
+ " must be system uid or original calling uid "
+ sourceRecord.launchedFromUid);
}
@@ -559,12 +853,12 @@
}
if (userId == UserHandle.USER_NULL) {
- userId = UserHandle.getUserId(sourceRecord.app.uid);
+ userId = UserHandle.getUserId(sourceRecord.app.mUid);
}
// TODO: Switch to user app stacks here.
try {
- return mAm.getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
+ return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
.setCallingUid(targetUid)
.setCallingPackage(targetPackage)
.setResolvedType(resolvedType)
@@ -593,19 +887,23 @@
}
}
+ int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+ return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
+ ALLOW_FULL_ONLY, name, null /* callerPackage */);
+ }
+
@Override
public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
- mAm.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
+ mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
}
- userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ALLOW_FULL_ONLY, "startVoiceActivity", null);
+ userId = handleIncomingUser(callingPid, callingUid, userId, "startVoiceActivity");
// TODO: Switch to user app stacks here.
- return mAm.getActivityStartController().obtainStarter(intent, "startVoiceActivity")
+ return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
.setCallingUid(callingUid)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
@@ -621,11 +919,10 @@
@Override
public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, Bundle bOptions, int userId) {
- mAm.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
- userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ALLOW_FULL_ONLY, "startAssistantActivity", null);
+ mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
+ userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
- return mAm.getActivityStartController().obtainStarter(intent, "startAssistantActivity")
+ return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
.setCallingUid(callingUid)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
@@ -637,18 +934,17 @@
@Override
public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
IRecentsAnimationRunner recentsAnimationRunner) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final ComponentName recentsComponent = mAm.getRecentTasks().getRecentsComponent();
- final int recentsUid = mAm.getRecentTasks().getRecentsComponentUid();
+ final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+ final int recentsUid = mRecentTasks.getRecentsComponentUid();
// Start a new recents animation
- final RecentsAnimation anim = new RecentsAnimation(mAm, mStackSupervisor,
- mAm.getActivityStartController(), mAm.mWindowManager, mAm.mUserController,
- callingPid);
+ final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
+ getActivityStartController(), mWindowManager, callingPid);
anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
recentsUid, assistDataReceiver);
}
@@ -659,7 +955,7 @@
@Override
public final int startActivityFromRecents(int taskId, Bundle bOptions) {
- mAm.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+ enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
"startActivityFromRecents()");
final int callingPid = Binder.getCallingPid();
@@ -707,20 +1003,22 @@
}
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
// finish.
- if (mAm.getLockTaskController().activityBlockedFromFinish(r)) {
+ if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}
- if (mAm.mController != null) {
+ // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
+ // We should consolidate.
+ if (mController != null) {
// Find the first activity that is not finishing.
ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
try {
- resumeOK = mAm.mController.activityResuming(next.packageName);
+ resumeOK = mController.activityResuming(next.packageName);
} catch (RemoteException e) {
- mAm.mController = null;
+ mController = null;
Watchdog.getInstance().setActivityController(null);
}
@@ -774,7 +1072,7 @@
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
// can finish.
final TaskRecord task = r.getTask();
- if (mAm.getLockTaskController().activityBlockedFromFinish(r)) {
+ if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}
return task.getStack().finishActivityAffinityLocked(r);
@@ -787,20 +1085,25 @@
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- ActivityStack stack = ActivityRecord.getStackLocked(token);
- if (stack != null) {
- ActivityRecord r =
- mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
- false /* processPausingActivities */, config);
- if (stopProfiling) {
- if ((mAm.mProfileProc == r.app) && mAm.mProfilerInfo != null) {
- mAm.clearProfilerLocked();
- }
+ try {
+ WindowProcessController proc = null;
+ synchronized (mGlobalLock) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack == null) {
+ return;
+ }
+ final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
+ false /* fromTimeout */, false /* processPausingActivities */, config);
+ if (r != null) {
+ proc = r.app;
+ }
+ if (stopProfiling && proc != null) {
+ proc.clearProfilerIfNeeded();
}
}
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
@Override
@@ -808,7 +1111,7 @@
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
ActivityRecord.activityResumedLocked(token);
- mAm.mWindowManager.notifyAppResumedFinished(token);
+ mWindowManager.notifyAppResumedFinished(token);
}
Binder.restoreCallingIdentity(origId);
}
@@ -844,7 +1147,7 @@
}
}
- mAm.trimApplications();
+ mAmInternal.trimApplications();
Binder.restoreCallingIdentity(origId);
}
@@ -923,11 +1226,30 @@
// update associated state if we're frontmost
if (r == mStackSupervisor.getResumedActivityLocked()) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
- mAm.applyUpdateLockStateLocked(r);
+ applyUpdateLockStateLocked(r);
}
}
}
+ void applyUpdateLockStateLocked(ActivityRecord r) {
+ // Modifications to the UpdateLock state are done on our handler, outside
+ // the activity manager's locks. The new state is determined based on the
+ // state *now* of the relevant activity record. The object is passed to
+ // the handler solely for logging detail, not to be consulted/modified.
+ final boolean nextState = r != null && r.immersive;
+ mH.post(() -> {
+ if (mUpdateLock.isHeld() != nextState) {
+ if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
+ "Applying new update lock state '" + nextState + "' for " + r);
+ if (nextState) {
+ mUpdateLock.acquire();
+ } else {
+ mUpdateLock.release();
+ }
+ }
+ });
+ }
+
@Override
public boolean isImmersive(IBinder token) {
synchronized (mGlobalLock) {
@@ -941,9 +1263,9 @@
@Override
public boolean isTopActivityImmersive() {
- mAm.enforceNotIsolatedCaller("isTopActivityImmersive");
+ enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
- final ActivityRecord r = mAm.getFocusedStack().topRunningActivityLocked();
+ final ActivityRecord r = getFocusedStack().topRunningActivityLocked();
return (r != null) ? r.immersive : false;
}
}
@@ -961,7 +1283,7 @@
if (self.isState(
ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
- mAm.mWindowManager.overridePendingAppTransition(packageName,
+ mWindowManager.overridePendingAppTransition(packageName,
enterAnim, exitAnim, null);
}
@@ -971,19 +1293,34 @@
@Override
public int getFrontActivityScreenCompatMode() {
- mAm.enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+ enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+ ApplicationInfo ai;
synchronized (mGlobalLock) {
- return mAm.mCompatModePackages.getFrontActivityScreenCompatModeLocked();
+ final ActivityRecord r = getFocusedStack().topRunningActivityLocked();
+ if (r == null) {
+ return ActivityManager.COMPAT_MODE_UNKNOWN;
+ }
+ ai = r.info.applicationInfo;
}
+
+ return mAmInternal.getPackageScreenCompatMode(ai);
}
@Override
public void setFrontActivityScreenCompatMode(int mode) {
- mAm.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setFrontActivityScreenCompatMode");
+ ApplicationInfo ai;
synchronized (mGlobalLock) {
- mAm.mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
+ final ActivityRecord r = getFocusedStack().topRunningActivityLocked();
+ if (r == null) {
+ Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
+ return;
+ }
+ ai = r.info.applicationInfo;
}
+
+ mAmInternal.setPackageScreenCompatMode(ai, mode);
}
@Override
@@ -1023,7 +1360,7 @@
if (translucentChanged) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
- mAm.mWindowManager.setAppFullscreen(token, true);
+ mWindowManager.setAppFullscreen(token, true);
return translucentChanged;
}
} finally {
@@ -1052,7 +1389,7 @@
r.getStack().convertActivityToTranslucent(r);
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mAm.mWindowManager.setAppFullscreen(token, false);
+ mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
} finally {
@@ -1095,11 +1432,11 @@
@Override
public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- ActivityStack focusedStack = mAm.getFocusedStack();
+ ActivityStack focusedStack = getFocusedStack();
if (focusedStack != null) {
return mStackSupervisor.getStackInfo(focusedStack.mStackId);
}
@@ -1112,7 +1449,7 @@
@Override
public void setFocusedStack(int stackId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
final long callingId = Binder.clearCallingIdentity();
try {
@@ -1135,7 +1472,7 @@
@Override
public void setFocusedTask(int taskId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
final long callingId = Binder.clearCallingIdentity();
try {
@@ -1156,7 +1493,7 @@
@Override
public boolean removeTask(int taskId) {
- mAm.enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
+ enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1213,7 +1550,7 @@
*/
@Override
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
- mAm.enforceNotIsolatedCaller("moveActivityTaskToBack");
+ enforceNotIsolatedCaller("moveActivityTaskToBack");
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -1231,7 +1568,7 @@
@Override
public Rect getTaskBounds(int taskId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
long ident = Binder.clearCallingIdentity();
Rect rect = new Rect();
try {
@@ -1265,7 +1602,7 @@
@Override
public ActivityManager.TaskDescription getTaskDescription(int id) {
synchronized (mGlobalLock) {
- mAm.enforceCallerIsRecentsOrHasPermission(
+ enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
@@ -1283,7 +1620,7 @@
toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
return;
}
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1315,7 +1652,7 @@
@Override
public String getCallingPackage(IBinder token) {
- synchronized (this) {
+ synchronized (mGlobalLock) {
ActivityRecord r = getCallingRecordLocked(token);
return r != null ? r.info.packageName : null;
}
@@ -1323,7 +1660,7 @@
@Override
public ComponentName getCallingActivity(IBinder token) {
- synchronized (this) {
+ synchronized (mGlobalLock) {
ActivityRecord r = getCallingRecordLocked(token);
return r != null ? r.intent.getComponent() : null;
}
@@ -1339,12 +1676,12 @@
@Override
public void unhandledBack() {
- mAm.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- mAm.getFocusedStack().unhandledBackLocked();
+ getFocusedStack().unhandledBackLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -1356,7 +1693,7 @@
*/
@Override
public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
- mAm.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
synchronized (mGlobalLock) {
@@ -1368,7 +1705,7 @@
void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
boolean fromRecents) {
- if (!mAm.checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), -1, -1, "Task to front")) {
SafeActivityOptions.abort(options);
return;
@@ -1380,7 +1717,7 @@
Slog.d(TAG, "Could not find task for id: "+ taskId);
return;
}
- if (mAm.getLockTaskController().isLockTaskModeViolation(task)) {
+ if (getLockTaskController().isLockTaskModeViolation(task)) {
Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
return;
}
@@ -1404,6 +1741,69 @@
SafeActivityOptions.abort(options);
}
+ boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
+ int callingPid, int callingUid, String name) {
+ if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
+ return true;
+ }
+
+ if (getRecentTasks().isCallerRecents(sourceUid)) {
+ return true;
+ }
+
+ int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ if (checkAllowAppSwitchUid(sourceUid)) {
+ return true;
+ }
+
+ // If the actual IPC caller is different from the logical source, then
+ // also see if they are allowed to control app switches.
+ if (callingUid != -1 && callingUid != sourceUid) {
+ perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ if (checkAllowAppSwitchUid(callingUid)) {
+ return true;
+ }
+ }
+
+ Slog.w(TAG, name + " request from " + sourceUid + " stopped");
+ return false;
+ }
+
+ private boolean checkAllowAppSwitchUid(int uid) {
+ ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
+ if (types != null) {
+ for (int i = types.size() - 1; i >= 0; i--) {
+ if (types.valueAt(i).intValue() == uid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setActivityController(IActivityController controller, boolean imAMonkey) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "setActivityController()");
+ synchronized (mGlobalLock) {
+ mController = controller;
+ mControllerIsAMonkey = imAMonkey;
+ Watchdog.getInstance().setActivityController(controller);
+ }
+ }
+
+ boolean isControllerAMonkey() {
+ synchronized (mGlobalLock) {
+ return mController != null && mControllerIsAMonkey;
+ }
+ }
+
@Override
public int getTaskForActivity(IBinder token, boolean onlyRoot) {
synchronized (mGlobalLock) {
@@ -1426,7 +1826,7 @@
synchronized (mGlobalLock) {
if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
- final boolean allowed = mAm.isGetTasksAllowed("getTasks", Binder.getCallingPid(),
+ final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
callingUid);
mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, callingUid, allowed);
@@ -1449,7 +1849,7 @@
@Override
public boolean willActivityBeVisible(IBinder token) {
- synchronized(this) {
+ synchronized (mGlobalLock) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
return stack.willActivityBeVisibleLocked(token);
@@ -1460,7 +1860,7 @@
@Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1483,7 +1883,7 @@
+ taskId + " to stack " + stackId);
}
if (stack.inSplitScreenPrimaryWindowingMode()) {
- mAm.mWindowManager.setDockedStackCreateState(
+ mWindowManager.setDockedStackCreateState(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
}
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
@@ -1497,7 +1897,7 @@
@Override
public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
boolean preserveWindows, boolean animate, int animationDuration) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -1549,7 +1949,7 @@
@Override
public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"setTaskWindowingModeSplitScreenPrimary()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -1567,7 +1967,7 @@
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
- mAm.mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ mWindowManager.setDockedStackCreateState(createMode, initialBounds);
final int windowingMode = task.getWindowingMode();
final ActivityStack stack = task.getStack();
if (toTop) {
@@ -1588,7 +1988,7 @@
*/
@Override
public void removeStacksInWindowingModes(int[] windowingModes) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"removeStacksInWindowingModes()");
synchronized (mGlobalLock) {
@@ -1603,7 +2003,7 @@
@Override
public void removeStacksWithActivityTypes(int[] activityTypes) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"removeStacksWithActivityTypes()");
synchronized (mGlobalLock) {
@@ -1620,23 +2020,23 @@
public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
int userId) {
final int callingUid = Binder.getCallingUid();
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
- false, ALLOW_FULL_ONLY, "getRecentTasks", null);
- final boolean allowed = mAm.isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+ userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, "getRecentTasks");
+ final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
callingUid);
- final boolean detailed = mAm.checkCallingPermission(
- android.Manifest.permission.GET_DETAILED_TASKS)
+ final boolean detailed = checkGetTasksPermission(
+ android.Manifest.permission.GET_DETAILED_TASKS, Binder.getCallingPid(),
+ UserHandle.getAppId(callingUid))
== PackageManager.PERMISSION_GRANTED;
synchronized (mGlobalLock) {
- return mAm.getRecentTasks().getRecentTasks(maxNum, flags, allowed, detailed, userId,
+ return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
callingUid);
}
}
@Override
public List<ActivityManager.StackInfo> getAllStackInfos() {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -1649,7 +2049,7 @@
@Override
public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -1662,13 +2062,13 @@
@Override
public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
final long callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
// Cancel the recents animation synchronously (do not hold the WM lock)
- mAm.mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+ mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
}
@@ -1690,7 +2090,7 @@
@Override
public void startSystemLockTaskMode(int taskId) throws RemoteException {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
// This makes inner call to look as if it was initiated by system.
long ident = Binder.clearCallingIdentity();
try {
@@ -1723,7 +2123,7 @@
*/
@Override
public void stopSystemLockTaskMode() throws RemoteException {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
stopLockTaskModeInternal(null, true /* isSystemCaller */);
}
@@ -1750,7 +2150,7 @@
// When a task is locked, dismiss the pinned stack if it exists
mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
- mAm.getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
+ getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1761,7 +2161,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mAm.getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
+ getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
}
// Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
// task and jumping straight into a call in the case of emergency call back.
@@ -1782,7 +2182,7 @@
@Override
public int getLockTaskModeState() {
synchronized (mGlobalLock) {
- return mAm.getLockTaskController().getLockTaskModeState();
+ return getLockTaskController().getLockTaskModeState();
}
}
@@ -1794,7 +2194,7 @@
r.setTaskDescription(td);
final TaskRecord task = r.getTask();
task.updateTaskDescription();
- mAm.mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
+ mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
}
}
}
@@ -1822,7 +2222,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mAm.getRecentTasks().getAppTasksList(callingUid, callingPackage);
+ return mRecentTasks.getAppTasksList(callingUid, callingPackage);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1862,14 +2262,25 @@
@Override
public void notifyEnterAnimationComplete(IBinder token) {
- mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(ENTER_ANIMATION_COMPLETE_MSG, token));
+ mH.post(() -> {
+ synchronized (mGlobalLock) {
+ ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null && r.attachedToProcess()) {
+ try {
+ r.app.getThread().scheduleEnterAnimationComplete(r.appToken);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ });
}
/** Called from an app when assist data is ready. */
@Override
public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
AssistContent content, Uri referrer) {
- PendingAssistExtras pae = (PendingAssistExtras)token;
+ PendingAssistExtras pae = (PendingAssistExtras) token;
synchronized (pae) {
pae.result = extras;
pae.structure = structure;
@@ -1893,13 +2304,13 @@
synchronized (mGlobalLock) {
buildAssistBundleLocked(pae, extras);
boolean exists = mPendingAssistExtras.remove(pae);
- mAm.mUiHandler.removeCallbacks(pae);
+ mUiHandler.removeCallbacks(pae);
if (!exists) {
// Timed out.
return;
}
- if ((sendReceiver=pae.receiver) != null) {
+ if ((sendReceiver = pae.receiver) != null) {
// Caller wants result sent back to them.
sendBundle = new Bundle();
sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
@@ -1927,7 +2338,7 @@
pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mAm.closeSystemDialogs("assist");
+ mAmInternal.closeSystemDialogs("assist");
try {
mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
@@ -1958,11 +2369,11 @@
throw new IllegalArgumentException("Intent " + intent
+ " must specify explicit component");
}
- if (thumbnail.getWidth() != mAm.mThumbnailWidth
- || thumbnail.getHeight() != mAm.mThumbnailHeight) {
+ if (thumbnail.getWidth() != mThumbnailWidth
+ || thumbnail.getHeight() != mThumbnailHeight) {
throw new IllegalArgumentException("Bad thumbnail size: got "
+ thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
- + mAm.mThumbnailWidth + "x" + mAm.mThumbnailHeight);
+ + mThumbnailWidth + "x" + mThumbnailHeight);
}
if (intent.getSelector() != null) {
intent.setSelector(null);
@@ -1989,7 +2400,7 @@
final TaskRecord task = stack.createTaskRecord(
mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
- if (!mAm.getRecentTasks().addToBottom(task)) {
+ if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
return INVALID_TASK_ID;
@@ -2008,7 +2419,7 @@
@Override
public Point getAppTaskThumbnailSize() {
synchronized (mGlobalLock) {
- return new Point(mAm.mThumbnailWidth, mAm.mThumbnailHeight);
+ return new Point(mThumbnailWidth, mThumbnailHeight);
}
}
@@ -2027,7 +2438,7 @@
@Override
public void resizeTask(int taskId, Rect bounds, int resizeMode) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2094,7 +2505,8 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- ProcessRecord app = mAm.getRecordForAppLocked(appInt);
+ WindowProcessController app =
+ mAm.getRecordForAppLocked(appInt).getWindowProcessController();
mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2105,7 +2517,7 @@
@Override
public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
int secondaryDisplayShowing) {
- if (mAm.checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
+ if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.DEVICE_POWER);
@@ -2118,21 +2530,32 @@
reportCurKeyguardUsageEventLocked(keyguardShowing);
}
try {
- mAm.mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
+ mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
secondaryDisplayShowing);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- mAm.mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, keyguardShowing ? 1 : 0, 0)
- .sendToTarget();
+ mH.post(() -> {
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardStateChanged(keyguardShowing);
+ }
+ });
+ }
+
+ void onScreenAwakeChanged(boolean isAwake) {
+ mH.post(() -> {
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+ }
+ });
}
@Override
public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, "getTaskDescriptionIcon");
final File passedIconFile = new File(filePath);
final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
@@ -2142,12 +2565,11 @@
throw new IllegalArgumentException("Bad file path: " + filePath
+ " passed for userId " + userId);
}
- return mAm.getRecentTasks().getTaskDescriptionIcon(filePath);
+ return mRecentTasks.getTaskDescriptionIcon(filePath);
}
@Override
- public void startInPlaceAnimationOnFrontMostApplication(Bundle opts)
- throws RemoteException {
+ public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
final ActivityOptions activityOptions = safeOptions != null
? safeOptions.getOptions(mStackSupervisor)
@@ -2158,15 +2580,15 @@
throw new IllegalArgumentException("Expected in-place ActivityOption " +
"with valid animation");
}
- mAm.mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
- mAm.mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+ mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+ mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
activityOptions.getCustomInPlaceResId());
- mAm.mWindowManager.executeAppTransition();
+ mWindowManager.executeAppTransition();
}
@Override
public void removeStack(int stackId) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -2188,7 +2610,7 @@
@Override
public void moveStackToDisplay(int stackId, int displayId) {
- mAm.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+ mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -2204,7 +2626,7 @@
@Override
public int createStackOnDisplay(int displayId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (mGlobalLock) {
final ActivityDisplay display =
mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
@@ -2246,17 +2668,17 @@
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"registerTaskStackListener()");
- mAm.mTaskChangeNotificationController.registerTaskStackListener(listener);
+ mTaskChangeNotificationController.registerTaskStackListener(listener);
}
/** Unregister a task stack listener so that it stops receiving callbacks. */
@Override
public void unregisterTaskStackListener(ITaskStackListener listener) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"unregisterTaskStackListener()");
- mAm.mTaskChangeNotificationController.unregisterTaskStackListener(listener);
+ mTaskChangeNotificationController.unregisterTaskStackListener(listener);
}
private void reportCurKeyguardUsageEventLocked(boolean keyguardShowing) {
@@ -2308,25 +2730,83 @@
synchronized (mGlobalLock) {
buildAssistBundleLocked(pae, pae.result);
mPendingAssistExtras.remove(pae);
- mAm.mUiHandler.removeCallbacks(pae);
+ mUiHandler.removeCallbacks(pae);
}
return pae.extras;
}
+ /**
+ * Binder IPC calls go through the public entry point.
+ * This can be called with or without the global lock held.
+ */
+ private static int checkCallingPermission(String permission) {
+ return checkPermission(
+ permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid()));
+ }
+
+ /** This can be called with or without the global lock held. */
+ void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+ if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
+ mAmInternal.enforceCallingPermission(permission, func);
+ }
+ }
+
+ @VisibleForTesting
+ int checkGetTasksPermission(String permission, int pid, int uid) {
+ return checkPermission(permission, pid, uid);
+ }
+
+ static int checkPermission(String permission, int pid, int uid) {
+ if (permission == null) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ return checkComponentPermission(permission, pid, uid, -1, true);
+ }
+
+ boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+ if (getRecentTasks().isCallerRecents(callingUid)) {
+ // Always allow the recents component to get tasks
+ return true;
+ }
+
+ boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ if (!allowed) {
+ if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
+ // Temporary compatibility: some existing apps on the system image may
+ // still be requesting the old permission and not switched to the new
+ // one; if so, we'll still allow them full access. This means we need
+ // to see if they are holding the old permission and are a system app.
+ try {
+ if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
+ allowed = true;
+ if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+ + " is using old GET_TASKS but privileged; allowing");
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+ + " does not hold REAL_GET_TASKS; limiting output");
+ }
+ return allowed;
+ }
+
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
int flags) {
- mAm.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"enqueueAssistContext()");
synchronized (mGlobalLock) {
- ActivityRecord activity = mAm.getFocusedStack().getTopActivity();
+ ActivityRecord activity = getFocusedStack().getTopActivity();
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
}
- if (activity.app == null || activity.app.thread == null) {
+ if (!activity.attachedToProcess()) {
Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
return null;
}
@@ -2346,7 +2826,7 @@
+ " couldn't be found");
return null;
}
- if (activity.app == null || activity.app.thread == null) {
+ if (!activity.attachedToProcess()) {
Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
return null;
}
@@ -2358,7 +2838,7 @@
extras.putAll(args);
}
extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
- extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
+ extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.mUid);
pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
userHandle);
@@ -2369,10 +2849,10 @@
mViSessionId++;
}
try {
- activity.app.thread.requestAssistContextExtras(activity.appToken, pae, requestType,
- mViSessionId, flags);
+ activity.app.getThread().requestAssistContextExtras(activity.appToken, pae,
+ requestType, mViSessionId, flags);
mPendingAssistExtras.add(pae);
- mAm.mUiHandler.postDelayed(pae, timeout);
+ mUiHandler.postDelayed(pae, timeout);
} catch (RemoteException e) {
Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
return null;
@@ -2449,7 +2929,7 @@
public boolean isAssistDataAllowedOnCurrentActivity() {
int userId;
synchronized (mGlobalLock) {
- final ActivityStack focusedStack = mAm.getFocusedStack();
+ final ActivityStack focusedStack = getFocusedStack();
if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
return false;
}
@@ -2469,7 +2949,7 @@
try {
synchronized (mGlobalLock) {
ActivityRecord caller = ActivityRecord.forTokenLocked(token);
- ActivityRecord top = mAm.getFocusedStack().getTopActivity();
+ ActivityRecord top = getFocusedStack().getTopActivity();
if (top != caller) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not current top " + top);
@@ -2528,17 +3008,17 @@
if (r == null) {
return;
}
- mAm.getLockTaskController().showLockTaskToast();
+ getLockTaskController().showLockTaskToast();
}
}
@Override
public void keyguardGoingAway(int flags) {
- mAm.enforceNotIsolatedCaller("keyguardGoingAway");
+ enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mAm.mKeyguardController.keyguardGoingAway(flags);
+ mKeyguardController.keyguardGoingAway(flags);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -2552,7 +3032,7 @@
*/
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
synchronized (mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
@@ -2613,7 +3093,7 @@
*/
@Override
public void dismissSplitScreenMode(boolean toTop) {
- mAm.enforceCallerIsRecentsOrHasPermission(
+ enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -2655,7 +3135,7 @@
*/
@Override
public void dismissPip(boolean animate, int animationDuration) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2683,7 +3163,7 @@
@Override
public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
synchronized (mGlobalLock) {
mSuppressResizeConfigChanges = suppress;
}
@@ -2697,7 +3177,7 @@
@Override
// TODO: API should just be about changing windowing modes...
public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"moveTasksToFullscreenStack()");
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
@@ -2727,10 +3207,10 @@
*/
@Override
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"moveTopActivityToPinnedStack()");
synchronized (mGlobalLock) {
- if (!mAm.mSupportsPictureInPicture) {
+ if (!mSupportsPictureInPicture) {
throw new IllegalStateException("moveTopActivityToPinnedStack:"
+ "Device doesn't support picture-in-picture mode");
}
@@ -2807,30 +3287,33 @@
}
final Runnable enterPipRunnable = () -> {
- // Only update the saved args from the args that are set
- r.pictureInPictureArgs.copyOnlySet(params);
- final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
- final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
- // Adjust the source bounds by the insets for the transition down
- final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
- mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
- "enterPictureInPictureMode");
- final PinnedActivityStack stack = r.getStack();
- stack.setPictureInPictureAspectRatio(aspectRatio);
- stack.setPictureInPictureActions(actions);
- MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
- r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
- logPictureInPictureArgs(params);
+ synchronized (mGlobalLock) {
+ // Only update the saved args from the args that are set
+ r.pictureInPictureArgs.copyOnlySet(params);
+ final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+ final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+ // Adjust the source bounds by the insets for the transition down
+ final Rect sourceBounds = new Rect(
+ r.pictureInPictureArgs.getSourceRectHint());
+ mStackSupervisor.moveActivityToPinnedStackLocked(
+ r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
+ final PinnedActivityStack stack = r.getStack();
+ stack.setPictureInPictureAspectRatio(aspectRatio);
+ stack.setPictureInPictureActions(actions);
+ MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
+ r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
+ logPictureInPictureArgs(params);
+ }
};
- if (mAm.isKeyguardLocked()) {
+ if (isKeyguardLocked()) {
// If the keyguard is showing or occluded, then try and dismiss it before
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
dismissKeyguard(token, new KeyguardDismissCallback() {
@Override
- public void onDismissSucceeded() throws RemoteException {
- mAm.mHandler.post(enterPipRunnable);
+ public void onDismissSucceeded() {
+ mH.post(enterPipRunnable);
}
}, null /* message */);
} else {
@@ -2899,7 +3382,7 @@
*/
private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
IBinder token, PictureInPictureParams params) {
- if (!mAm.mSupportsPictureInPicture) {
+ if (!mSupportsPictureInPicture) {
throw new IllegalStateException(caller
+ ": Device doesn't support picture-in-picture mode.");
}
@@ -2916,7 +3399,7 @@
}
if (params.hasSetAspectRatio()
- && !mAm.mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
+ && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
params.getAspectRatio())) {
final float minAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
@@ -2935,7 +3418,7 @@
@Override
public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
- mAm.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+ enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
if (r == null) {
@@ -2950,7 +3433,7 @@
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2965,7 +3448,7 @@
@Override
public void setSplitScreenResizing(boolean resizing) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2976,9 +3459,20 @@
}
}
+ /**
+ * Check that we have the features required for VR-related API calls, and throw an exception if
+ * not.
+ */
+ void enforceSystemHasVrFeature() {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
+ throw new UnsupportedOperationException("VR mode not supported on this device!");
+ }
+ }
+
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
- mAm.enforceSystemHasVrFeature();
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -3005,7 +3499,7 @@
// Update associated state if this activity is currently focused
if (r == mStackSupervisor.getResumedActivityLocked()) {
- mAm.applyUpdateVrModeLocked(r);
+ applyUpdateVrModeLocked(r);
}
return 0;
}
@@ -3018,7 +3512,7 @@
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
synchronized (mGlobalLock) {
- ActivityRecord activity = mAm.getFocusedStack().getTopActivity();
+ ActivityRecord activity = getFocusedStack().getTopActivity();
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
@@ -3052,18 +3546,18 @@
/** Notifies all listeners when the pinned stack animation starts. */
@Override
public void notifyPinnedStackAnimationStarted() {
- mAm.mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
+ mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
}
/** Notifies all listeners when the pinned stack animation ends. */
@Override
public void notifyPinnedStackAnimationEnded() {
- mAm.mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
+ mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
}
@Override
public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3076,7 +3570,7 @@
@Override
public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
- mAm.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
+ mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
synchronized (mGlobalLock) {
// Check if display is initialized in AM.
@@ -3089,14 +3583,14 @@
return false;
}
- if (values == null && mAm.mWindowManager != null) {
+ if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
- values = mAm.mWindowManager.computeNewConfiguration(displayId);
+ values = mWindowManager.computeNewConfiguration(displayId);
}
- if (mAm.mWindowManager != null) {
+ if (mWindowManager != null) {
// Update OOM levels based on display size.
- mAm.mProcessList.applyDisplaySize(mAm.mWindowManager);
+ mAm.mProcessList.applyDisplaySize(mWindowManager);
}
final long origId = Binder.clearCallingIdentity();
@@ -3104,7 +3598,7 @@
if (values != null) {
Settings.System.clearConfiguration(values);
}
- mAm.updateDisplayOverrideConfigurationLocked(values, null /* starting */,
+ updateDisplayOverrideConfigurationLocked(values, null /* starting */,
false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
return mTmpUpdateConfigurationResult.changes != 0;
} finally {
@@ -3115,17 +3609,17 @@
@Override
public boolean updateConfiguration(Configuration values) {
- mAm.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
+ mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
synchronized (mGlobalLock) {
- if (values == null && mAm.mWindowManager != null) {
+ if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
- values = mAm.mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
}
- if (mAm.mWindowManager != null) {
+ if (mWindowManager != null) {
// Update OOM levels based on display size.
- mAm.mProcessList.applyDisplaySize(mAm.mWindowManager);
+ mAm.mProcessList.applyDisplaySize(mWindowManager);
}
final long origId = Binder.clearCallingIdentity();
@@ -3133,7 +3627,7 @@
if (values != null) {
Settings.System.clearConfiguration(values);
}
- mAm.updateConfigurationLocked(values, null, false, false /* persistent */,
+ updateConfigurationLocked(values, null, false, false /* persistent */,
UserHandle.USER_NULL, false /* deferResume */,
mTmpUpdateConfigurationResult);
return mTmpUpdateConfigurationResult.changes != 0;
@@ -3147,13 +3641,13 @@
public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
CharSequence message) {
if (message != null) {
- mAm.enforceCallingPermission(
+ mAmInternal.enforceCallingPermission(
Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
}
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mAm.mKeyguardController.dismissKeyguard(token, callback, message);
+ mKeyguardController.dismissKeyguard(token, callback, message);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -3162,7 +3656,7 @@
@Override
public void cancelTaskWindowTransition(int taskId) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"cancelTaskWindowTransition()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -3182,7 +3676,7 @@
@Override
public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
- mAm.enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+ enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task;
@@ -3223,11 +3717,11 @@
@Override
public @UserIdInt
int getLastResumedActivityUserId() {
- mAm.enforceCallingPermission(
+ mAmInternal.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
synchronized (mGlobalLock) {
if (mAm.mLastResumedActivity == null) {
- return mAm.mUserController.getCurrentUserId();
+ return getCurrentUserId();
}
return mAm.mLastResumedActivity.userId;
}
@@ -3237,13 +3731,13 @@
public void updateLockTaskFeatures(int userId, int flags) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
- mAm.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
"updateLockTaskFeatures()");
}
synchronized (mGlobalLock) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
Integer.toHexString(flags));
- mAm.getLockTaskController().updateLockTaskFeatures(userId, flags);
+ getLockTaskController().updateLockTaskFeatures(userId, flags);
}
}
@@ -3281,7 +3775,7 @@
@Override
public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
- mAm.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimations");
definition.setCallingPid(Binder.getCallingPid());
synchronized (mGlobalLock) {
@@ -3301,13 +3795,13 @@
@Override
public void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
- mAm.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimationForNextActivityStart");
adapter.setCallingPid(Binder.getCallingPid());
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- mAm.getActivityStartController().registerRemoteAnimationForNextActivityStart(
+ getActivityStartController().registerRemoteAnimationForNextActivityStart(
packageName, adapter);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -3327,4 +3821,853 @@
}
}
}
+
+ @Override
+ public void setVrThread(int tid) {
+ enforceSystemHasVrFeature();
+ synchronized (mGlobalLock) {
+ synchronized (mAm.mPidsSelfLocked) {
+ final int pid = Binder.getCallingPid();
+ final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
+ mVrController.setVrThreadLocked(tid, pid, proc.getWindowProcessController());
+ }
+ }
+ }
+
+ @Override
+ public void setPersistentVrThread(int tid) {
+ if (checkCallingPermission(Manifest.permission.RESTRICTED_VR_ACCESS)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: setPersistentVrThread() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + Manifest.permission.RESTRICTED_VR_ACCESS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ enforceSystemHasVrFeature();
+ synchronized (mGlobalLock) {
+ synchronized (mAm.mPidsSelfLocked) {
+ final int pid = Binder.getCallingPid();
+ final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
+ mVrController.setPersistentVrThreadLocked(tid, pid, proc);
+ }
+ }
+ }
+
+ @Override
+ public void stopAppSwitches() {
+ enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
+ synchronized (mGlobalLock) {
+ mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
+ mDidAppSwitch = false;
+ getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
+ }
+ }
+
+ @Override
+ public void resumeAppSwitches() {
+ enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
+ synchronized (mGlobalLock) {
+ // Note that we don't execute any pending app switches... we will
+ // let those wait until either the timeout, or the next start
+ // activity request.
+ mAppSwitchesAllowedTime = 0;
+ }
+ }
+
+ void onStartActivitySetDidAppSwitch() {
+ if (mDidAppSwitch) {
+ // This is the second allowed switch since we stopped switches, so now just generally
+ // allow switches. Use case:
+ // - user presses home (switches disabled, switch to home, mDidAppSwitch now true);
+ // - user taps a home icon (coming from home so allowed, we hit here and now allow
+ // anyone to switch again).
+ mAppSwitchesAllowedTime = 0;
+ } else {
+ mDidAppSwitch = true;
+ }
+ }
+
+ /** @return whether the system should disable UI modes incompatible with VR mode. */
+ boolean shouldDisableNonVrUiLocked() {
+ return mVrController.shouldDisableNonVrUiLocked();
+ }
+
+ void applyUpdateVrModeLocked(ActivityRecord r) {
+ // VR apps are expected to run in a main display. If an app is turning on VR for
+ // itself, but lives in a dynamic stack, then make sure that it is moved to the main
+ // fullscreen stack before enabling VR Mode.
+ // TODO: The goal of this code is to keep the VR app on the main display. When the
+ // stack implementation changes in the future, keep in mind that the use of the fullscreen
+ // stack is a means to move the activity to the main display and a moveActivityToDisplay()
+ // option would be a better choice here.
+ if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
+ Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+ + " to main stack for VR");
+ final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
+ moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+ }
+ mH.post(() -> {
+ if (!mVrController.onVrModeChanged(r)) {
+ return;
+ }
+ synchronized (mGlobalLock) {
+ final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
+ mWindowManager.disableNonVrUi(disableNonVrUi);
+ if (disableNonVrUi) {
+ // If we are in a VR mode where Picture-in-Picture mode is unsupported,
+ // then remove the pinned stack.
+ mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ }
+ }
+ });
+ }
+
+ ActivityStack getFocusedStack() {
+ return mStackSupervisor.getFocusedStack();
+ }
+
+ /** Pokes the task persister. */
+ void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+ mRecentTasks.notifyTaskPersisterLocked(task, flush);
+ }
+
+ void onTopProcChangedLocked(WindowProcessController proc) {
+ mVrController.onTopProcChangedLocked(proc);
+ }
+
+ boolean isKeyguardLocked() {
+ return mKeyguardController.isKeyguardLocked();
+ }
+
+ boolean isNextTransitionForward() {
+ int transit = mWindowManager.getPendingAppTransition();
+ return transit == TRANSIT_ACTIVITY_OPEN
+ || transit == TRANSIT_TASK_OPEN
+ || transit == TRANSIT_TASK_TO_FRONT;
+ }
+
+ void dumpVrControllerLocked(PrintWriter pw) {
+ pw.println(" mVrController=" + mVrController);
+ }
+
+ void writeVrControllerToProto(ProtoOutputStream proto, long fieldId) {
+ mVrController.writeToProto(proto, fieldId);
+ }
+
+ int getCurrentUserId() {
+ return mAmInternal.getCurrentUserId();
+ }
+
+ private void enforceNotIsolatedCaller(String caller) {
+ if (UserHandle.isIsolated(Binder.getCallingUid())) {
+ throw new SecurityException("Isolated process not allowed to call " + caller);
+ }
+ }
+
+ /**
+ * Current global configuration information. Contains general settings for the entire system,
+ * also corresponds to the merged configuration of the default display.
+ */
+ Configuration getGlobalConfiguration() {
+ return mStackSupervisor.getConfiguration();
+ }
+
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale) {
+ return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
+ }
+
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean deferResume) {
+ // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
+ return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
+ UserHandle.USER_NULL, deferResume);
+ }
+
+ void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ updateConfigurationLocked(values, null, false, true, userId,
+ false /* deferResume */);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ void updateUserConfiguration() {
+ synchronized (mGlobalLock) {
+ final Configuration configuration = new Configuration(getGlobalConfiguration());
+ final int currentUserId = mAmInternal.getCurrentUserId();
+ Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
+ currentUserId, Settings.System.canWrite(mContext));
+ updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
+ false /* persistent */, currentUserId, false /* deferResume */);
+ }
+ }
+
+ private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean persistent, int userId, boolean deferResume) {
+ return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
+ deferResume, null /* result */);
+ }
+
+ /**
+ * Do either or both things: (1) change the current configuration, and (2)
+ * make sure the given activity is running with the (now) current
+ * configuration. Returns true if the activity has been left running, or
+ * false if <var>starting</var> is being destroyed to match the new
+ * configuration.
+ *
+ * @param userId is only used when persistent parameter is set to true to persist configuration
+ * for that particular user
+ */
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean persistent, int userId, boolean deferResume,
+ ActivityTaskManagerService.UpdateConfigurationResult result) {
+ int changes = 0;
+ boolean kept = true;
+
+ if (mWindowManager != null) {
+ mWindowManager.deferSurfaceLayout();
+ }
+ try {
+ if (values != null) {
+ changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
+ deferResume);
+ }
+
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ } finally {
+ if (mWindowManager != null) {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ if (result != null) {
+ result.changes = changes;
+ result.activityRelaunched = !kept;
+ }
+ return kept;
+ }
+
+ /**
+ * Returns true if this configuration change is interesting enough to send an
+ * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+ */
+ private static boolean isSplitConfigurationChange(int configDiff) {
+ return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+ }
+
+ /** Update default (global) configuration and notify listeners about changes. */
+ private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
+ boolean persistent, int userId, boolean deferResume) {
+ mTempConfig.setTo(getGlobalConfiguration());
+ final int changes = mTempConfig.updateFrom(values);
+ if (changes == 0) {
+ // Since calling to Activity.setRequestedOrientation leads to freezing the window with
+ // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
+ // performDisplayOverrideConfigUpdate in order to send the new display configuration
+ // (even if there are no actual changes) to unfreeze the window.
+ performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
+ return 0;
+ }
+
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
+ "Updating global configuration to: " + values);
+
+ EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
+ StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
+ values.colorMode,
+ values.densityDpi,
+ values.fontScale,
+ values.hardKeyboardHidden,
+ values.keyboard,
+ values.keyboardHidden,
+ values.mcc,
+ values.mnc,
+ values.navigation,
+ values.navigationHidden,
+ values.orientation,
+ values.screenHeightDp,
+ values.screenLayout,
+ values.screenWidthDp,
+ values.smallestScreenWidthDp,
+ values.touchscreen,
+ values.uiMode);
+
+
+ if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
+ final LocaleList locales = values.getLocales();
+ int bestLocaleIndex = 0;
+ if (locales.size() > 1) {
+ if (mSupportedSystemLocales == null) {
+ mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+ }
+ bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
+ }
+ SystemProperties.set("persist.sys.locale",
+ locales.get(bestLocaleIndex).toLanguageTag());
+ LocaleList.setDefault(locales, bestLocaleIndex);
+ mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
+ locales.get(bestLocaleIndex)));
+ }
+
+ mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+ mTempConfig.seq = mConfigurationSeq;
+
+ // Update stored global config and notify everyone about the change.
+ mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+ Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
+ // TODO(multi-display): Update UsageEvents#Event to include displayId.
+ mAm.mUsageStatsService.reportConfigurationChange(
+ mTempConfig, mAmInternal.getCurrentUserId());
+
+ // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
+ mAm.updateShouldShowDialogsLocked(mTempConfig);
+
+ AttributeCache ac = AttributeCache.instance();
+ if (ac != null) {
+ ac.updateConfiguration(mTempConfig);
+ }
+
+ // Make sure all resources in our process are updated right now, so that anyone who is going
+ // to retrieve resource values after we return will be sure to get the new ones. This is
+ // especially important during boot, where the first config change needs to guarantee all
+ // resources have that config before following boot code is executed.
+ mAm.mSystemThread.applyConfigurationToResources(mTempConfig);
+
+ // We need another copy of global config because we're scheduling some calls instead of
+ // running them in place. We need to be sure that object we send will be handled unchanged.
+ final Configuration configCopy = new Configuration(mTempConfig);
+ if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
+ Message msg = mAm.mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
+ msg.obj = configCopy;
+ msg.arg1 = userId;
+ mAm.mHandler.sendMessage(msg);
+ }
+
+ for (int i = mAm.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord app = mAm.mLruProcesses.get(i);
+ try {
+ if (app.thread != null) {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
+ + app.processName + " new config " + configCopy);
+ getLifecycleManager().scheduleTransaction(app.thread,
+ ConfigurationChangeItem.obtain(configCopy));
+ }
+ } catch (Exception e) {
+ Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+ }
+ }
+
+ Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+ intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ if (initLocale || !mAm.mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+ mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+
+ // Send a broadcast to PackageInstallers if the configuration change is interesting
+ // for the purposes of installing additional splits.
+ if (!initLocale && isSplitConfigurationChange(changes)) {
+ intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+ // Typically only app stores will have this permission.
+ String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+ mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+
+ // Override configuration of the default display duplicates global config, so we need to
+ // update it also. This will also notify WindowManager about changes.
+ performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+ DEFAULT_DISPLAY);
+
+ return changes;
+ }
+
+ boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean deferResume, int displayId) {
+ return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
+ displayId, null /* result */);
+ }
+
+ /**
+ * Updates override configuration specific for the selected display. If no config is provided,
+ * new one will be computed in WM based on current display info.
+ */
+ boolean updateDisplayOverrideConfigurationLocked(Configuration values,
+ ActivityRecord starting, boolean deferResume, int displayId,
+ ActivityTaskManagerService.UpdateConfigurationResult result) {
+ int changes = 0;
+ boolean kept = true;
+
+ if (mWindowManager != null) {
+ mWindowManager.deferSurfaceLayout();
+ }
+ try {
+ if (values != null) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // Override configuration of the default display duplicates global config, so
+ // we're calling global config update instead for default display. It will also
+ // apply the correct override config.
+ changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
+ false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
+ } else {
+ changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
+ }
+ }
+
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ } finally {
+ if (mWindowManager != null) {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ if (result != null) {
+ result.changes = changes;
+ result.activityRelaunched = !kept;
+ }
+ return kept;
+ }
+
+ private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
+ int displayId) {
+ mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ final int changes = mTempConfig.updateFrom(values);
+ if (changes != 0) {
+ Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+ + mTempConfig + " for displayId=" + displayId);
+ mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+
+ final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+ if (isDensityChange && displayId == DEFAULT_DISPLAY) {
+ mAm.mAppWarnings.onDensityChanged();
+
+ mAm.killAllBackgroundProcessesExcept(N,
+ ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ }
+ }
+
+ // Update the configuration with WM first and check if any of the stacks need to be resized
+ // due to the configuration change. If so, resize the stacks now and do any relaunches if
+ // necessary. This way we don't need to relaunch again afterwards in
+ // ensureActivityConfiguration().
+ if (mWindowManager != null) {
+ final int[] resizedStacks =
+ mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
+ if (resizedStacks != null) {
+ for (int stackId : resizedStacks) {
+ resizeStackWithBoundsFromWindowManager(stackId, deferResume);
+ }
+ }
+ }
+
+ return changes;
+ }
+
+ /** Helper method that requests bounds from WM and applies them to stack. */
+ private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
+ final Rect newStackBounds = new Rect();
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+ // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
+ if (stack == null) {
+ final StringWriter writer = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(writer);
+ mStackSupervisor.dumpDisplays(printWriter);
+ printWriter.flush();
+
+ Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
+ }
+
+ stack.getBoundsForNewConfiguration(newStackBounds);
+ mStackSupervisor.resizeStackLocked(
+ stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+ null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+ false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
+ }
+
+ /** Applies latest configuration and/or visibility updates if needed. */
+ private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
+ boolean kept = true;
+ final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
+ // mainStack is null during startup.
+ if (mainStack != null) {
+ if (changes != 0 && starting == null) {
+ // If the configuration changed, and the caller is not already
+ // in the process of starting an activity, then find the top
+ // activity to check if its configuration needs to change.
+ starting = mainStack.topRunningActivityLocked();
+ }
+
+ if (starting != null) {
+ kept = starting.ensureActivityConfiguration(changes,
+ false /* preserveWindow */);
+ // And we need to make sure at this point that all other activities
+ // are made visible with the correct configuration.
+ mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+ !PRESERVE_WINDOWS);
+ }
+ }
+
+ return kept;
+ }
+
+ void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
+ if (true || Build.IS_USER) {
+ return;
+ }
+
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.allowThreadDiskWrites();
+ try {
+ File tracesDir = new File("/data/anr");
+ File tracesFile = null;
+ try {
+ tracesFile = File.createTempFile("app_slow", null, tracesDir);
+
+ StringBuilder sb = new StringBuilder();
+ Time tobj = new Time();
+ tobj.set(System.currentTimeMillis());
+ sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
+ sb.append(": ");
+ TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
+ sb.append(" since ");
+ sb.append(msg);
+ FileOutputStream fos = new FileOutputStream(tracesFile);
+ fos.write(sb.toString().getBytes());
+ if (app == null) {
+ fos.write("\n*** No application process!".getBytes());
+ }
+ fos.close();
+ FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
+ return;
+ }
+
+ if (app != null && app.getPid() > 0) {
+ ArrayList<Integer> firstPids = new ArrayList<Integer>();
+ firstPids.add(app.getPid());
+ dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
+ }
+
+ File lastTracesFile = null;
+ File curTracesFile = null;
+ for (int i=9; i>=0; i--) {
+ String name = String.format(Locale.US, "slow%02d.txt", i);
+ curTracesFile = new File(tracesDir, name);
+ if (curTracesFile.exists()) {
+ if (lastTracesFile != null) {
+ curTracesFile.renameTo(lastTracesFile);
+ } else {
+ curTracesFile.delete();
+ }
+ }
+ lastTracesFile = curTracesFile;
+ }
+ tracesFile.renameTo(curTracesFile);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ final class H extends Handler {
+ public H(Looper looper) {
+ super(looper, null, true);
+ }
+ }
+
+ final class UiHandler extends Handler {
+
+ public UiHandler() {
+ super(com.android.server.UiThread.get().getLooper(), null, true);
+ }
+ }
+
+ final class LocalService extends ActivityTaskManagerInternal {
+ @Override
+ public SleepToken acquireSleepToken(String tag, int displayId) {
+ Preconditions.checkNotNull(tag);
+ return mAm.acquireSleepToken(tag, displayId);
+ }
+
+ @Override
+ public ComponentName getHomeActivityForUser(int userId) {
+ synchronized (mGlobalLock) {
+ ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
+ return homeActivity == null ? null : homeActivity.realActivity;
+ }
+ }
+
+ @Override
+ public void onLocalVoiceInteractionStarted(IBinder activity,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+ synchronized (mGlobalLock) {
+ mAm.onLocalVoiceInteractionStartedLocked(activity, voiceSession, voiceInteractor);
+ }
+ }
+
+ @Override
+ public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+ reasons, timestamp);
+ }
+ }
+
+ @Override
+ public void notifyAppTransitionFinished() {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.notifyAppTransitionDone();
+ }
+ }
+
+ @Override
+ public void notifyAppTransitionCancelled() {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.notifyAppTransitionDone();
+ }
+ }
+
+ @Override
+ public List<IBinder> getTopVisibleActivities() {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.getTopVisibleActivities();
+ }
+ }
+
+ @Override
+ public void notifyDockedStackMinimizedChanged(boolean minimized) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.setDockedStackMinimized(minimized);
+ }
+ }
+
+ @Override
+ public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
+ Bundle bOptions) {
+ Preconditions.checkNotNull(intents, "intents");
+ final String[] resolvedTypes = new String[intents.length];
+
+ // UID of the package on user userId.
+ // "= 0" is needed because otherwise catch(RemoteException) would make it look like
+ // packageUid may not be initialized.
+ int packageUid = 0;
+ final long ident = Binder.clearCallingIdentity();
+
+ try {
+ for (int i = 0; i < intents.length; i++) {
+ resolvedTypes[i] =
+ intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
+ }
+
+ packageUid = AppGlobals.getPackageManager().getPackageUid(
+ packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ synchronized (mGlobalLock) {
+ return getActivityStartController().startActivitiesInPackage(
+ packageUid, packageName,
+ intents, resolvedTypes, null /* resultTo */,
+ SafeActivityOptions.fromBundle(bOptions), userId,
+ false /* validateIncomingUser */);
+ }
+ }
+
+ @Override
+ public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
+ Intent intent, Bundle options, int userId) {
+ return ActivityTaskManagerService.this.startActivityAsUser(
+ caller, callerPacakge, intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
+ false /*validateIncomingUser*/);
+ }
+
+ @Override
+ public void notifyKeyguardFlagsChanged(@Nullable Runnable callback) {
+ synchronized (mGlobalLock) {
+
+ // We might change the visibilities here, so prepare an empty app transition which
+ // might be overridden later if we actually change visibilities.
+ final boolean wasTransitionSet =
+ mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
+ if (!wasTransitionSet) {
+ mWindowManager.prepareAppTransition(TRANSIT_NONE,
+ false /* alwaysKeepCurrent */);
+ }
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+
+ // If there was a transition set already we don't want to interfere with it as we
+ // might be starting it too early.
+ if (!wasTransitionSet) {
+ mWindowManager.executeAppTransition();
+ }
+ }
+ if (callback != null) {
+ callback.run();
+ }
+ }
+
+ @Override
+ public void notifyKeyguardTrustedChanged() {
+ synchronized (mGlobalLock) {
+ if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ }
+ }
+ }
+
+ /**
+ * Called after virtual display Id is updated by
+ * {@link com.android.server.vr.Vr2dDisplay} with a specific
+ * {@param vrVr2dDisplayId}.
+ */
+ @Override
+ public void setVr2dDisplayId(int vr2dDisplayId) {
+ if (DEBUG_STACK) Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
+ synchronized (mGlobalLock) {
+ mVr2dDisplayId = vr2dDisplayId;
+ }
+ }
+
+ @Override
+ public void setFocusedActivity(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ throw new IllegalArgumentException(
+ "setFocusedActivity: No activity record matching token=" + token);
+ }
+ if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
+ r, "setFocusedActivity")) {
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+ }
+ }
+
+ @Override
+ public void registerScreenObserver(ScreenObserver observer) {
+ mScreenObservers.add(observer);
+ }
+
+ @Override
+ public boolean isCallerRecents(int callingUid) {
+ return getRecentTasks().isCallerRecents(callingUid);
+ }
+
+ @Override
+ public boolean isRecentsComponentHomeActivity(int userId) {
+ return getRecentTasks().isRecentsComponentHomeActivity(userId);
+ }
+
+ @Override
+ public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+ ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+ }
+
+ @Override
+ public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+ ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
+ }
+
+ @Override
+ public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
+ synchronized (mGlobalLock) {
+ mActiveVoiceInteractionServiceComponent = component;
+ }
+ }
+
+ @Override
+ public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
+ if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
+ return;
+ }
+ synchronized (mGlobalLock) {
+ ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
+ if (types == null) {
+ if (uid < 0) {
+ return;
+ }
+ types = new ArrayMap<>();
+ mAllowAppSwitchUids.put(userId, types);
+ }
+ if (uid < 0) {
+ types.remove(type);
+ } else {
+ types.put(type, uid);
+ }
+ }
+ }
+
+ @Override
+ public void onUserStopped(int userId) {
+ synchronized (mGlobalLock) {
+ getRecentTasks().unloadUserDataFromMemoryLocked(userId);
+ mAllowAppSwitchUids.remove(userId);
+ }
+ }
+
+ @Override
+ public boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+ synchronized (mGlobalLock) {
+ return ActivityTaskManagerService.this.isGetTasksAllowed(
+ caller, callingPid, callingUid);
+ }
+ }
+
+ @Override
+ public void onProcessAdded(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ mProcessNames.put(proc.mName, proc.mUid, proc);
+ }
+ }
+
+ @Override
+ public void onProcessRemoved(String name, int uid) {
+ synchronized (mGlobalLock) {
+ mProcessNames.remove(name, uid);
+ }
+ }
+
+ @Override
+ public void onCleanUpApplicationRecord(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ if (proc == mHomeProcess) {
+ mHomeProcess = null;
+ }
+ if (proc == mPreviousProcess) {
+ mPreviousProcess = null;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 68c63a2..cde633d 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
| WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
- if (mProc.persistent) {
+ if (mProc.isPersistent()) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 75090d4..7db98b3 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
@@ -45,7 +44,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
import android.util.SparseArray;
@@ -57,7 +55,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Set;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -314,9 +311,9 @@
}
void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
- app.crashing = false;
+ app.setCrashing(false);
app.crashingReport = null;
- app.notResponding = false;
+ app.setNotResponding(false);
app.notRespondingReport = null;
if (app.anrDialog == fromDialog) {
app.anrDialog = null;
@@ -409,7 +406,7 @@
// If a persistent app is stuck in a crash loop, the device isn't very
// usable, so we want to consider sending out a rescue party.
- if (r != null && r.persistent) {
+ if (r != null && r.isPersistent()) {
RescueParty.notePersistentAppCrash(mContext, r.uid);
}
@@ -479,7 +476,7 @@
final Set<String> cats = task.intent != null
? task.intent.getCategories() : null;
if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
- mService.getActivityStartController().startActivityInPackage(
+ mService.mActivityTaskManager.getActivityStartController().startActivityInPackage(
task.mCallingUid, callingPid, callingUid, task.mCallingPackage,
task.intent, null, null, null, 0, 0,
new SafeActivityOptions(ActivityOptions.makeBasic()),
@@ -493,8 +490,8 @@
long orig = Binder.clearCallingIdentity();
try {
// Kill it with fire!
- mService.mStackSupervisor.handleAppCrashLocked(r);
- if (!r.persistent) {
+ mService.mStackSupervisor.handleAppCrashLocked(r.getWindowProcessController());
+ if (!r.isPersistent()) {
mService.removeProcessLocked(r, false, false, "crash");
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
@@ -532,7 +529,7 @@
String shortMsg, String longMsg,
String stackTrace, long timeMillis,
int callingPid, int callingUid) {
- if (mService.mController == null) {
+ if (mService.mActivityTaskManager.mController == null) {
return false;
}
@@ -540,7 +537,7 @@
String name = r != null ? r.processName : null;
int pid = r != null ? r.pid : callingPid;
int uid = r != null ? r.info.uid : callingUid;
- if (!mService.mController.appCrashed(name, pid,
+ if (!mService.mActivityTaskManager.mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
&& "Native crash".equals(crashInfo.exceptionClassName)) {
@@ -563,7 +560,7 @@
return true;
}
} catch (RemoteException e) {
- mService.mController = null;
+ mService.mActivityTaskManager.mController = null;
Watchdog.getInstance().setActivityController(null);
}
return false;
@@ -571,11 +568,11 @@
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
- app.crashing = true;
+ app.setCrashing(true);
app.crashingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
startAppProblemLocked(app);
- app.stopFreezingAllLocked();
+ app.getWindowProcessController().stopFreezingActivities();
return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
data);
}
@@ -643,7 +640,7 @@
return null;
}
- if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
+ if (!r.isCrashing() && !r.isNotResponding() && !r.forceCrashReport) {
return null;
}
@@ -654,10 +651,10 @@
report.time = timeMillis;
report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- if (r.crashing || r.forceCrashReport) {
+ if (r.isCrashing() || r.forceCrashReport) {
report.type = ApplicationErrorReport.TYPE_CRASH;
report.crashInfo = crashInfo;
- } else if (r.notResponding) {
+ } else if (r.isNotResponding()) {
report.type = ApplicationErrorReport.TYPE_ANR;
report.anrInfo = new ApplicationErrorReport.AnrInfo();
@@ -715,8 +712,8 @@
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
- mService.mStackSupervisor.handleAppCrashLocked(app);
- if (!app.persistent) {
+ mService.mStackSupervisor.handleAppCrashLocked(app.getWindowProcessController());
+ if (!app.isPersistent()) {
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
// need to keep it running. If a persistent process is actually
@@ -744,7 +741,7 @@
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
final TaskRecord affectedTask =
- mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app, reason);
+ mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app.getWindowProcessController(), reason);
if (data != null) {
data.task = affectedTask;
}
@@ -762,21 +759,11 @@
// replaced by a third-party app, clear the package preferred activities from packages
// with a home activity running in the process to prevent a repeatedly crashing app
// from blocking the user to manually clear the list.
- final ArrayList<ActivityRecord> activities = app.activities;
- if (app == mService.mHomeProcess && activities.size() > 0
- && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isActivityTypeHome()) {
- Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
- try {
- ActivityThread.getPackageManager()
- .clearPackagePreferredActivities(r.packageName);
- } catch (RemoteException c) {
- // pm is in same process, this will never happen.
- }
- }
- }
+ final WindowProcessController proc = app.getWindowProcessController();
+ final WindowProcessController homeProc = mService.mActivityTaskManager.mHomeProcess;
+ if (proc == homeProc && proc.hasActivities()
+ && (homeProc.mInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ proc.clearPackagePreferredForHomeActivities();
}
if (!app.isolated) {
@@ -887,16 +874,16 @@
ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
- if (mService.mController != null) {
+ if (mService.mActivityTaskManager.mController != null) {
try {
// 0 == continue, -1 = kill process immediately
- int res = mService.mController.appEarlyNotResponding(
+ int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
app.processName, app.pid, annotation);
if (res < 0 && app.pid != MY_PID) {
app.kill("anr", true);
}
} catch (RemoteException e) {
- mService.mController = null;
+ mService.mActivityTaskManager.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
@@ -917,10 +904,10 @@
if (mService.mShuttingDown) {
Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
return;
- } else if (app.notResponding) {
+ } else if (app.isNotResponding()) {
Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
return;
- } else if (app.crashing) {
+ } else if (app.isCrashing()) {
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
} else if (app.killedByAm) {
@@ -933,7 +920,7 @@
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
- app.notResponding = true;
+ app.setNotResponding(true);
// Log the ANR to the event log.
EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
@@ -946,8 +933,8 @@
isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
if (!isSilentANR) {
int parentPid = app.pid;
- if (parent != null && parent.app != null && parent.app.pid > 0) {
- parentPid = parent.app.pid;
+ if (parent != null && parent.app != null && parent.app.getPid() > 0) {
+ parentPid = parent.app.getPid();
}
if (parentPid != app.pid) firstPids.add(parentPid);
@@ -958,7 +945,7 @@
if (r != null && r.thread != null) {
int pid = r.pid;
if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
- if (r.persistent) {
+ if (r.isPersistent()) {
firstPids.add(pid);
if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
} else if (r.treatLikeActivity) {
@@ -1054,10 +1041,10 @@
mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
cpuInfo, tracesFile, null);
- if (mService.mController != null) {
+ if (mService.mActivityTaskManager.mController != null) {
try {
// 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
- int res = mService.mController.appNotResponding(
+ int res = mService.mActivityTaskManager.mController.appNotResponding(
app.processName, app.pid, info.toString());
if (res != 0) {
if (res < 0 && app.pid != MY_PID) {
@@ -1070,7 +1057,7 @@
return;
}
} catch (RemoteException e) {
- mService.mController = null;
+ mService.mActivityTaskManager.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
@@ -1100,12 +1087,12 @@
private void makeAppNotRespondingLocked(ProcessRecord app,
String activity, String shortMsg, String longMsg) {
- app.notResponding = true;
+ app.setNotResponding(true);
app.notRespondingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
activity, shortMsg, longMsg, null);
startAppProblemLocked(app);
- app.stopFreezingAllLocked();
+ app.getWindowProcessController().stopFreezingActivities();
}
void handleShowAnrUi(Message msg) {
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 8a88a69..7c983ff 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -157,7 +157,7 @@
System.currentTimeMillis(), null);
}
- app.notResponding = false;
+ app.setNotResponding(false);
app.notRespondingReport = null;
if (app.anrDialog == AppNotRespondingDialog.this) {
app.anrDialog = null;
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index 5f5a504..c22dedc 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -20,7 +20,6 @@
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.IAppTask;
import android.app.IApplicationThread;
import android.content.Intent;
@@ -35,12 +34,12 @@
* only the process that calls getAppTasks() can call the AppTask methods.
*/
class AppTaskImpl extends IAppTask.Stub {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private int mTaskId;
private int mCallingUid;
- public AppTaskImpl(ActivityManagerService service, int taskId, int callingUid) {
+ public AppTaskImpl(ActivityTaskManagerService service, int taskId, int callingUid) {
mService = service;
mTaskId = taskId;
mCallingUid = callingUid;
@@ -57,7 +56,7 @@
public void finishAndRemoveTask() {
checkCaller();
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
// We remove the task from recents to preserve backwards
@@ -75,7 +74,7 @@
public ActivityManager.RecentTaskInfo getTaskInfo() {
checkCaller();
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
@@ -115,7 +114,7 @@
int callingUser = UserHandle.getCallingUserId();
TaskRecord tr;
IApplicationThread appThread;
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
@@ -141,7 +140,7 @@
public void setExcludeFromRecents(boolean exclude) {
checkCaller();
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 14d1498..395b0da 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -17,13 +17,12 @@
package com.android.server.am;
import static android.app.ActivityManager.ASSIST_CONTEXT_FULL;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_NONE;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
-import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
import android.content.Context;
import android.graphics.Bitmap;
@@ -49,7 +48,6 @@
public static final String KEY_RECEIVER_EXTRA_COUNT = "count";
public static final String KEY_RECEIVER_EXTRA_INDEX = "index";
- private IActivityManager mService;
private IWindowManager mWindowManager;
private Context mContext;
private AppOpsManager mAppOpsManager;
@@ -118,14 +116,13 @@
* This can be {@link AppOpsManager#OP_NONE} to indicate that
* screenshots should never be fetched.
*/
- public AssistDataRequester(Context context, IActivityManager service,
+ public AssistDataRequester(Context context,
IWindowManager windowManager, AppOpsManager appOpsManager,
AssistDataRequesterCallbacks callbacks, Object callbacksLock,
int requestStructureAppOps, int requestScreenshotAppOps) {
mCallbacks = callbacks;
mCallbacksLock = callbacksLock;
mWindowManager = windowManager;
- mService = service;
mContext = context;
mAppOpsManager = appOpsManager;
mRequestStructureAppOps = requestStructureAppOps;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c9a26cb..a9fd51d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -304,7 +304,7 @@
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
- app.repProcState);
+ app.getReportedProcState());
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -492,7 +492,7 @@
// correctly ordered with other one-way calls.
try {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser, app.repProcState);
+ data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
// DeadObjectException when the process isn't actually dead.
//} catch (DeadObjectException ex) {
@@ -618,7 +618,7 @@
}
if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
- || filter.receiverList.app.crashing)) {
+ || filter.receiverList.app.isCrashing())) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ " to " + filter.receiverList + ": process gone or crashing");
skip = true;
@@ -885,7 +885,7 @@
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
mPendingBroadcast.curApp.pid);
- isDead = proc == null || proc.crashing;
+ isDead = proc == null || proc.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessNames.get(
@@ -1210,7 +1210,7 @@
+ " (uid " + r.callingUid + ")");
skip = true;
}
- if (r.curApp != null && r.curApp.crashing) {
+ if (r.curApp != null && r.curApp.isCrashing()) {
// If the target process is crashing, just skip it.
Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
+ " to " + r.curApp + ": process crashing");
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index c6947ab..38254b8 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -219,25 +219,10 @@
: ActivityManager.COMPAT_MODE_DISABLED;
}
- public boolean getFrontActivityAskCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r == null) {
- return false;
- }
- return getPackageAskCompatModeLocked(r.packageName);
- }
-
public boolean getPackageAskCompatModeLocked(String packageName) {
return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
}
- public void setFrontActivityAskCompatModeLocked(boolean ask) {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r != null) {
- setPackageAskCompatModeLocked(r.packageName, ask);
- }
- }
-
public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
}
@@ -255,23 +240,6 @@
}
}
- public int getFrontActivityScreenCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r == null) {
- return ActivityManager.COMPAT_MODE_UNKNOWN;
- }
- return computeCompatModeLocked(r.info.applicationInfo);
- }
-
- public void setFrontActivityScreenCompatModeLocked(int mode) {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r == null) {
- Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
- return;
- }
- setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
- }
-
public int getPackageScreenCompatModeLocked(String packageName) {
ApplicationInfo ai = null;
try {
@@ -297,7 +265,7 @@
setPackageScreenCompatModeLocked(ai, mode);
}
- private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
+ void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
final String packageName = ai.packageName;
int curFlags = getPackageFlags(packageName);
@@ -349,7 +317,7 @@
scheduleWrite();
- final ActivityStack stack = mService.getFocusedStack();
+ final ActivityStack stack = mService.mActivityTaskManager.getFocusedStack();
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 30fa72f..657e0eb 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -19,6 +19,13 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -27,15 +34,7 @@
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_UNSET;
-import android.app.ActivityManagerInternal.SleepToken;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -44,6 +43,7 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 4330d2c..6415c3e 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -38,7 +38,7 @@
* registered {@link LaunchParamsModifier}s.
*/
class LaunchParamsController {
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
// Temporary {@link LaunchParams} for internal calculations. This is kept separate from
@@ -48,7 +48,7 @@
private final LaunchParams mTmpCurrent = new LaunchParams();
private final LaunchParams mTmpResult = new LaunchParams();
- LaunchParamsController(ActivityManagerService service) {
+ LaunchParamsController(ActivityTaskManagerService service) {
mService = service;
}
@@ -125,7 +125,7 @@
try {
if (mTmpParams.hasPreferredDisplay()
&& mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
- mService.mActivityTaskManager.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
+ mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
}
if (mTmpParams.hasWindowingMode()
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 9348023..6e42aee 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -29,7 +29,6 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
-import java.net.InetSocketAddress;
/**
* Set up a Unix domain socket that debuggerd will connect() to in
@@ -226,7 +225,7 @@
}
if (pr != null) {
// Don't attempt crash reporting for persistent apps
- if (pr.persistent) {
+ if (pr.isPersistent()) {
if (DEBUG) {
Slog.v(TAG, "Skipping report for persistent app " + pr);
}
@@ -260,7 +259,7 @@
// even though the process will vanish as soon as we let
// debuggerd proceed.
synchronized (mAm) {
- pr.crashing = true;
+ pr.setCrashing(true);
pr.forceCrashReport = true;
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index e0aa2a2..db09165 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -343,12 +343,12 @@
allIntents[allIntents.length-1] = finalIntent;
allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
- res = owner.getActivityStartController().startActivitiesInPackage(
+ res = owner.mActivityTaskManager.getActivityStartController().startActivitiesInPackage(
uid, key.packageName, allIntents, allResolvedTypes,
resultTo, mergedOptions, userId,
false /* validateIncomingUser */);
} else {
- res = owner.getActivityStartController().startActivityInPackage(uid,
+ res = owner.mActivityTaskManager.getActivityStartController().startActivityInPackage(uid,
callingPid, callingUid, key.packageName, finalIntent,
resolvedType, resultTo, resultWho, requestCode, 0,
mergedOptions, userId, null, "PendingIntentRecord",
diff --git a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
index 77713f5..877d896 100644
--- a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
+++ b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
@@ -33,9 +33,9 @@
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
private final Handler mHandler;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
- PendingRemoteAnimationRegistry(ActivityManagerService service, Handler handler) {
+ PendingRemoteAnimationRegistry(ActivityTaskManagerService service, Handler handler) {
mService = service;
mHandler = handler;
}
@@ -74,7 +74,7 @@
this.packageName = packageName;
this.adapter = adapter;
mHandler.postDelayed(() -> {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
final Entry entry = mEntries.get(packageName);
if (entry == this) {
mEntries.remove(packageName);
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 4062f45..edc6e53 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -55,7 +55,7 @@
void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
boolean fromFullscreen) {
if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
- mService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId, true /* onTop */);
+ mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
} else {
getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds,
animationDuration, fromFullscreen);
@@ -100,7 +100,7 @@
// It is guaranteed that the activities requiring the update will be in the pinned stack at
// this point (either reparented before the animation into PiP, or before reparenting after
// the animation out of PiP)
- synchronized(this) {
+ synchronized (mService) {
ArrayList<TaskRecord> tasks = getAllTasks();
for (int i = 0; i < tasks.size(); i++ ) {
mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index caf52e3..5a44ab6 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -54,7 +54,7 @@
* Full information about a particular process that
* is currently running.
*/
-final class ProcessRecord {
+final class ProcessRecord implements WindowProcessListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
private final ActivityManagerService mService; // where we came from
@@ -65,7 +65,37 @@
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
+ final PackageList pkgList = new PackageList();
+ final class PackageList {
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>();
+
+ ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) {
+ mWindowProcessController.addPackage(key);
+ return mPkgList.put(key, value);
+ }
+
+ void clear() {
+ mPkgList.clear();
+ mWindowProcessController.clearPackageList();
+ }
+
+ int size() {
+ return mPkgList.size();
+ }
+
+ String keyAt(int index) {
+ return mPkgList.keyAt(index);
+ }
+
+ public ProcessStats.ProcessStateHolder valueAt(int index) {
+ return mPkgList.valueAt(index);
+ }
+
+ boolean containsKey(Object key) {
+ return mPkgList.containsKey(key);
+ }
+ }
+
final ProcessList.ProcStateMemTracker procStateMemTracker
= new ProcessList.ProcStateMemTracker();
UidRecord uidRecord; // overall state of process's uid.
@@ -78,7 +108,7 @@
int pid; // The process of this application; 0 if none
String procStatFile; // path to /proc/<pid>/stat
int[] gids; // The gids this process was launched with
- String requiredAbi; // The ABI this process was launched with
+ private String mRequiredAbi;// The ABI this process was launched with
String instructionSet; // The instruction set this process was launched with
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
@@ -96,12 +126,11 @@
int curAdj; // Current OOM adjustment for this process
int setAdj; // Last set OOM adjustment for this process
int verifiedAdj; // The last adjustment that was verified as actually being set
- int curSchedGroup; // Currently desired scheduling class
+ private int mCurSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
- int vrThreadTid; // Thread currently set for VR scheduling
int trimMemoryLevel; // Last selected memory trimming level
int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
- int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+ private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
int pssStatType; // The type of stat collection that we are currently requesting
@@ -112,7 +141,7 @@
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
boolean hasClientActivities; // Are there any client services with activities?
boolean hasStartedServices; // Are there any started services running in this process?
- boolean foregroundServices; // Running any services that are foreground?
+ private boolean mHasForegroundServices; // Running any services that are foreground?
boolean foregroundActivities; // Running any activities that are foreground?
boolean repForegroundActivities; // Last reported foreground activities.
boolean systemNoUi; // This is a system process, but not currently showing UI.
@@ -173,10 +202,8 @@
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
- // all activities running in the process
- final ArrayList<ActivityRecord> activities = new ArrayList<>();
- // any tasks this process had run root activities in
- final ArrayList<TaskRecord> recentTasks = new ArrayList<>();
+ // Controller for driving the process state on the window manager side.
+ final private WindowProcessController mWindowProcessController;
// all ServiceRecord running in this process
final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
@@ -194,11 +221,11 @@
String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
boolean execServicesFg; // do we need to be executing services in the foreground?
- boolean persistent; // always keep this application running?
- boolean crashing; // are we in the process of crashing?
+ private boolean mPersistent;// always keep this application running?
+ private boolean mCrashing; // are we in the process of crashing?
Dialog crashDialog; // dialog being displayed due to crash.
boolean forceCrashReport; // suppress normal auto-dismiss of crash dialog & report UI?
- boolean notResponding; // does the app have a not responding dialog?
+ private boolean mNotResponding; // does the app have a not responding dialog?
Dialog anrDialog; // dialog being displayed due to app not resp.
boolean removed; // has app package been removed from device?
boolean debugging; // was app launched for debugging?
@@ -259,7 +286,7 @@
}
}
pw.println("}");
- pw.print(prefix); pw.print("requiredAbi="); pw.print(requiredAbi);
+ pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
pw.print(" instructionSet="); pw.println(instructionSet);
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
@@ -324,15 +351,12 @@
pw.print(" setRaw="); pw.print(setRawAdj);
pw.print(" cur="); pw.print(curAdj);
pw.print(" set="); pw.println(setAdj);
- pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
+ pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
- if (vrThreadTid != 0) {
- pw.print(prefix); pw.print("vrThreadTid="); pw.println(vrThreadTid);
- }
pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
- pw.print(" repProcState="); pw.print(repProcState);
+ pw.print(" mRepProcState="); pw.print(mRepProcState);
pw.print(" pssProcState="); pw.print(pssProcState);
pw.print(" setProcState="); pw.print(setProcState);
pw.print(" lastStateTime=");
@@ -349,8 +373,8 @@
pw.print(" hasOverlayUi="); pw.print(hasOverlayUi);
pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
}
- if (foregroundServices || forcingToImportant != null) {
- pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
+ if (mHasForegroundServices || forcingToImportant != null) {
+ pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
pw.print(" forcingToImportant="); pw.println(forcingToImportant);
}
if (reportedInteraction || fgInteractionTime != 0) {
@@ -366,8 +390,8 @@
}
pw.println();
}
- if (persistent || removed) {
- pw.print(prefix); pw.print("persistent="); pw.print(persistent);
+ if (mPersistent || removed) {
+ pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
pw.print(" removed="); pw.println(removed);
}
if (hasClientActivities || foregroundActivities || repForegroundActivities) {
@@ -407,16 +431,16 @@
pw.print(" killedByAm="); pw.print(killedByAm);
pw.print(" waitingToKill="); pw.println(waitingToKill);
}
- if (debugging || crashing || crashDialog != null || notResponding
+ if (debugging || mCrashing || crashDialog != null || mNotResponding
|| anrDialog != null || bad) {
pw.print(prefix); pw.print("debugging="); pw.print(debugging);
- pw.print(" crashing="); pw.print(crashing);
+ pw.print(" mCrashing="); pw.print(mCrashing);
pw.print(" "); pw.print(crashDialog);
- pw.print(" notResponding="); pw.print(notResponding);
+ pw.print(" mNotResponding="); pw.print(mNotResponding);
pw.print(" " ); pw.print(anrDialog);
pw.print(" bad="); pw.print(bad);
- // crashing or notResponding is always set before errorReportReceiver
+ // mCrashing or mNotResponding is always set before errorReportReceiver
if (errorReportReceiver != null) {
pw.print(" errorReportReceiver=");
pw.print(errorReportReceiver.flattenToShortString());
@@ -431,18 +455,7 @@
pw.print(prefix); pw.print("isolatedEntryPointArgs=");
pw.println(Arrays.toString(isolatedEntryPointArgs));
}
- if (activities.size() > 0) {
- pw.print(prefix); pw.println("Activities:");
- for (int i=0; i<activities.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(activities.get(i));
- }
- }
- if (recentTasks.size() > 0) {
- pw.print(prefix); pw.println("Recent Tasks:");
- for (int i=0; i<recentTasks.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(recentTasks.get(i));
- }
- }
+ mWindowProcessController.dump(pw, prefix);
if (services.size() > 0) {
pw.print(prefix); pw.println("Services:");
for (int i=0; i<services.size(); i++) {
@@ -498,17 +511,20 @@
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
maxAdj = ProcessList.UNKNOWN_ADJ;
curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
- persistent = false;
+ mPersistent = false;
removed = false;
lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
+ mWindowProcessController = new WindowProcessController(
+ mService.mActivityTaskManager, info, processName, uid, userId, this, this);
+ pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
public void setPid(int _pid) {
pid = _pid;
+ mWindowProcessController.setPid(pid);
procStatFile = null;
shortStringName = null;
stringName = null;
@@ -519,7 +535,7 @@
final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
origBase.makeInactive();
}
baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
@@ -538,15 +554,17 @@
}
}
thread = _thread;
+ mWindowProcessController.setThread(thread);
}
public void makeInactive(ProcessStatsService tracker) {
thread = null;
+ mWindowProcessController.setThread(null);
final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
origBase.makeInactive();
}
baseProcessTracker = null;
@@ -560,11 +578,24 @@
}
}
- public void clearRecentTasks() {
- for (int i = recentTasks.size() - 1; i >= 0; i--) {
- recentTasks.get(i).clearRootProcess();
- }
- recentTasks.clear();
+ boolean hasActivities() {
+ return mWindowProcessController.hasActivities();
+ }
+
+ void clearActivities() {
+ mWindowProcessController.clearActivities();
+ }
+
+ boolean hasActivitiesOrRecentTasks() {
+ return mWindowProcessController.hasActivitiesOrRecentTasks();
+ }
+
+ boolean hasRecentTasks() {
+ return mWindowProcessController.hasRecentTasks();
+ }
+
+ void clearRecentTasks() {
+ mWindowProcessController.clearRecentTasks();
}
/**
@@ -572,12 +603,8 @@
* to the user. See HistoryRecord.isInterestingToUserLocked()
*/
public boolean isInterestingToUserLocked() {
- final int size = activities.size();
- for (int i = 0 ; i < size ; i++) {
- ActivityRecord r = activities.get(i);
- if (r.isInterestingToUserLocked()) {
- return true;
- }
+ if (mWindowProcessController.isInterestingToUser()) {
+ return true;
}
final int servicesSize = services.size();
@@ -590,14 +617,6 @@
return false;
}
- public void stopFreezingAllLocked() {
- int i = activities.size();
- while (i > 0) {
- i--;
- activities.get(i).stopFreezingScreenLocked(true);
- }
- }
-
public void unlinkDeathRecipient() {
if (deathRecipient != null && thread != null) {
thread.asBinder().unlinkToDeath(deathRecipient, 0);
@@ -676,7 +695,7 @@
} else {
pendingStart = false;
}
- if (!persistent) {
+ if (!mPersistent) {
killed = true;
killedByAm = true;
}
@@ -697,7 +716,7 @@
proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
}
}
- proto.write(ProcessRecordProto.PERSISTENT, persistent);
+ proto.write(ProcessRecordProto.PERSISTENT, mPersistent);
proto.end(token);
}
@@ -806,8 +825,8 @@
}
public void forceProcessStateUpTo(int newState) {
- if (repProcState > newState) {
- curProcState = repProcState = newState;
+ if (mRepProcState > newState) {
+ curProcState = mRepProcState = newState;
}
}
@@ -819,7 +838,7 @@
if (baseProcessTracker != null) {
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), now, pkgList);
+ tracker.getMemFactorLocked(), now, pkgList.mPkgList);
if (N != 1) {
for (int i=0; i<N; i++) {
ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
@@ -856,4 +875,128 @@
}
return list;
}
+
+ WindowProcessController getWindowProcessController() {
+ return mWindowProcessController;
+ }
+
+ void setCurrentSchedulingGroup(int curSchedGroup) {
+ mCurSchedGroup = curSchedGroup;
+ mWindowProcessController.setCurrentSchedulingGroup(curSchedGroup);
+ }
+
+ int getCurrentSchedulingGroup() {
+ return mCurSchedGroup;
+ }
+
+ void setReportedProcState(int repProcState) {
+ mRepProcState = repProcState;
+ mWindowProcessController.setReportedProcState(repProcState);
+ }
+
+ int getReportedProcState() {
+ return mRepProcState;
+ }
+
+ void setCrashing(boolean crashing) {
+ mCrashing = crashing;
+ mWindowProcessController.setCrashing(crashing);
+ }
+
+ boolean isCrashing() {
+ return mCrashing;
+ }
+
+ void setNotResponding(boolean notResponding) {
+ mNotResponding = notResponding;
+ mWindowProcessController.setNotResponding(notResponding);
+ }
+
+ boolean isNotResponding() {
+ return mNotResponding;
+ }
+
+ void setPersistent(boolean persistent) {
+ mPersistent = persistent;
+ mWindowProcessController.setPersistent(persistent);
+ }
+
+ boolean isPersistent() {
+ return mPersistent;
+ }
+
+ public void setRequiredAbi(String requiredAbi) {
+ mRequiredAbi = requiredAbi;
+ mWindowProcessController.setRequiredAbi(requiredAbi);
+ }
+
+ String getRequiredAbi() {
+ return mRequiredAbi;
+ }
+
+ void setHasForegroundServices(boolean hasForegroundServices) {
+ mHasForegroundServices = hasForegroundServices;
+ mWindowProcessController.setHasForegroundServices(hasForegroundServices);
+ }
+
+ boolean hasForegroundServices() {
+ return mHasForegroundServices;
+ }
+
+ @Override
+ public void clearProfilerIfNeeded() {
+ synchronized (mService) {
+ if (mService.mProfileProc == null || mService.mProfilerInfo == null
+ || mService.mProfileProc != this) {
+ return;
+ }
+ mService.clearProfilerLocked();
+ }
+ }
+
+ @Override
+ public void updateServiceConnectionActivities() {
+ synchronized (mService) {
+ mService.mServices.updateServiceConnectionActivitiesLocked(this);
+ }
+ }
+
+ @Override
+ public void setPendingUiClean(boolean pendingUiClean) {
+ synchronized (mService) {
+ this.pendingUiClean = true;
+ }
+ }
+
+ @Override
+ public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+ synchronized (mService) {
+ pendingUiClean = true;
+ forceProcessStateUpTo(newState);
+ }
+ }
+
+ @Override
+ public void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+ boolean activityChange, boolean updateOomAdj) {
+ synchronized (mService) {
+ if (updateServiceConnectionActivities) {
+ mService.mServices.updateServiceConnectionActivitiesLocked(this);
+ }
+ if (updateLru) {
+ mService.updateLruProcessLocked(this, activityChange, null);
+ }
+ if (updateOomAdj) {
+ mService.updateOomAdjLocked();
+ }
+ }
+ }
+
+ @Override
+ public void setRemoved(boolean removed) {
+ synchronized (mService) {
+ this.removed = removed;
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 2f52002..29c1657 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -32,7 +32,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
@@ -203,7 +202,7 @@
&& (filterByClasses == null
|| filterByClasses.contains(provider.name.getClassName())));
if (sameComponent
- && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
+ && (provider.proc == null || evenPersistent || !provider.proc.isPersistent())) {
if (!doit) {
return true;
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 810b9fd..749589b 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -138,7 +138,8 @@
* Save recent tasks information across reboots.
*/
private final TaskPersister mTaskPersister;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
+ private final ActivityStackSupervisor mSupervisor;
private final UserController mUserController;
/**
@@ -180,20 +181,22 @@
private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
@VisibleForTesting
- RecentTasks(ActivityManagerService service, TaskPersister taskPersister,
+ RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister,
UserController userController) {
mService = service;
+ mSupervisor = mService.mStackSupervisor;
mUserController = userController;
mTaskPersister = taskPersister;
mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
mHasVisibleRecentTasks = true;
}
- RecentTasks(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) {
+ RecentTasks(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) {
final File systemDir = Environment.getDataSystemDirectory();
final Resources res = service.mContext.getResources();
mService = service;
- mUserController = service.mUserController;
+ mSupervisor = mService.mStackSupervisor;
+ mUserController = service.mAm.mUserController;
mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
@@ -286,7 +289,7 @@
* @return whether the home app is also the active handler of recent tasks.
*/
boolean isRecentsComponentHomeActivity(int userId) {
- final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
+ final ComponentName defaultHomeActivity = mService.mAm.getPackageManagerInternalLocked()
.getDefaultHomeActivity(userId);
return defaultHomeActivity != null && mRecentsComponent != null &&
defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
@@ -442,7 +445,7 @@
}
void flush() {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
syncPersistentTaskIdsLocked();
}
mTaskPersister.flush();
@@ -512,7 +515,7 @@
&& tr.realActivitySuspended != suspended) {
tr.realActivitySuspended = suspended;
if (suspended) {
- mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ mSupervisor.removeTaskByIdLocked(tr.taskId, false,
REMOVE_FROM_RECENTS, "suspended-package");
}
notifyTaskPersisterLocked(tr, false);
@@ -540,7 +543,7 @@
if (tr.userId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
+ mSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
"remove-package-task");
}
}
@@ -557,7 +560,7 @@
final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
- mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ mSupervisor.removeTaskByIdLocked(tr.taskId, false,
REMOVE_FROM_RECENTS, "disabled-package");
}
}
@@ -716,7 +719,7 @@
boolean getTasksAllowed, boolean getDetailedTasks, int userId, int callingUid) {
final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
- if (!mService.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
+ if (!mService.mAm.isUserRunning(userId, FLAG_AND_UNLOCKED)) {
Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
return ParceledListSlice.emptyList();
}
@@ -1206,7 +1209,7 @@
*/
protected boolean isTrimmable(TaskRecord task) {
final ActivityStack stack = task.getStack();
- final ActivityStack homeStack = mService.mStackSupervisor.mHomeStack;
+ final ActivityStack homeStack = mSupervisor.mHomeStack;
// No stack for task, just trim it
if (stack == null) {
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index bd49bd1..1c7ad3f 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -54,11 +54,10 @@
private static final String TAG = RecentsAnimation.class.getSimpleName();
private static final boolean DEBUG = false;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final ActivityStartController mActivityStartController;
private final WindowManagerService mWindowManager;
- private final UserController mUserController;
private final ActivityDisplay mDefaultDisplay;
private final int mCallingPid;
@@ -68,15 +67,14 @@
// The stack to restore the target stack behind when the animation is finished
private ActivityStack mRestoreTargetBehindStack;
- RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
+ RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
ActivityStartController activityStartController, WindowManagerService wm,
- UserController userController, int callingPid) {
- mService = am;
+ int callingPid) {
+ mService = atm;
mStackSupervisor = stackSupervisor;
mDefaultDisplay = stackSupervisor.getDefaultDisplay();
mActivityStartController = activityStartController;
mWindowManager = wm;
- mUserController = userController;
mCallingPid = callingPid;
}
@@ -122,7 +120,7 @@
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
- mService.setRunningRemoteAnimation(mCallingPid, true);
+ mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true);
mWindowManager.deferSurfaceLayout();
try {
@@ -132,7 +130,7 @@
mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
assistDataReceiver, recentsComponent.getPackageName());
- mAssistDataRequester = new AssistDataRequester(mService.mContext, mService,
+ mAssistDataRequester = new AssistDataRequester(mService.mContext,
mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
true /* fetchData */, false /* fetchScreenshots */,
@@ -165,7 +163,7 @@
.setCallingUid(recentsUid)
.setCallingPackage(recentsComponent.getPackageName())
.setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
- .setMayWait(mUserController.getCurrentUserId())
+ .setMayWait(mService.getCurrentUserId())
.execute();
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
mWindowManager.executeAppTransition();
@@ -210,7 +208,7 @@
}
private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+ mWindowManager.getRecentsAnimationController()
+ " reorderMode=" + reorderMode);
@@ -232,7 +230,7 @@
mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
}
- mService.setRunningRemoteAnimation(mCallingPid, false);
+ mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, false);
mWindowManager.inSurfaceTransaction(() -> {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
@@ -317,7 +315,7 @@
if (runSychronously) {
finishAnimation(reorderMode);
} else {
- mService.mHandler.post(() -> finishAnimation(reorderMode));
+ mService.mH.post(() -> finishAnimation(reorderMode));
}
}
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index 55d17a9..fef3b86 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -117,7 +117,7 @@
* @param callerApp The record of the caller.
*/
ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
- @Nullable ProcessRecord callerApp,
+ @Nullable WindowProcessController callerApp,
ActivityStackSupervisor supervisor) throws SecurityException {
if (mOriginalOptions != null) {
checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
@@ -186,13 +186,13 @@
}
private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
- @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor,
+ @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
ActivityOptions options, int callingPid, int callingUid) {
// If a launch task id is specified, then ensure that the caller is the recents
// component or has the START_TASKS_FROM_RECENTS permission
if (options.getLaunchTaskId() != INVALID_TASK_ID
&& !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
- final int startInTaskPerm = supervisor.mService.checkPermission(
+ final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
START_TASKS_FROM_RECENTS, callingPid, callingUid);
if (startInTaskPerm == PERMISSION_DENIED) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
@@ -230,7 +230,7 @@
// Check permission for remote animations
final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
- if (adapter != null && supervisor.mService.checkPermission(
+ if (adapter != null && supervisor.mService.mAm.checkPermission(
CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
!= PERMISSION_GRANTED) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 7896e2d..efb80be 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -52,7 +52,8 @@
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
- private final ActivityManagerService mService;
+ // Global lock used by the service the instantiate objects of this class.
+ private final Object mServiceLock;
private final ActivityStackSupervisor mStackSupervisor;
private final Handler mHandler;
@@ -149,7 +150,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case LOG_STACK_STATE_MSG: {
- synchronized (mService) {
+ synchronized (mServiceLock) {
mStackSupervisor.logStackState();
}
break;
@@ -209,15 +210,15 @@
}
}
- public TaskChangeNotificationController(ActivityManagerService service,
+ public TaskChangeNotificationController(Object serviceLock,
ActivityStackSupervisor stackSupervisor, Handler handler) {
- mService = service;
+ mServiceLock = serviceLock;
mStackSupervisor = stackSupervisor;
mHandler = new MainHandler(handler.getLooper());
}
public void registerTaskStackListener(ITaskStackListener listener) {
- synchronized (mService) {
+ synchronized (mServiceLock) {
if (listener != null) {
if (Binder.getCallingPid() == android.os.Process.myPid()) {
if (!mLocalTaskStackListeners.contains(listener)) {
@@ -231,7 +232,7 @@
}
public void unregisterTaskStackListener(ITaskStackListener listener) {
- synchronized (mService) {
+ synchronized (mServiceLock) {
if (listener != null) {
if (Binder.getCallingPid() == android.os.Process.myPid()) {
mLocalTaskStackListeners.remove(listener);
@@ -243,7 +244,7 @@
}
private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
- synchronized (mService) {
+ synchronized (mServiceLock) {
for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
try {
// Make a one-way callback to the listener
@@ -257,7 +258,7 @@
}
private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
- synchronized (mService) {
+ synchronized (mServiceLock) {
for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
try {
callback.accept(mLocalTaskStackListeners.get(i), message);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 2689d6a..481bb2b 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -83,7 +83,7 @@
private static final String TAG_TASK = "task";
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final RecentTasks mRecentTasks;
private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
@@ -125,7 +125,7 @@
ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
- ActivityManagerService service, RecentTasks recentTasks) {
+ ActivityTaskManagerService service, RecentTasks recentTasks) {
final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
if (legacyImagesDir.exists()) {
@@ -565,7 +565,7 @@
private void writeTaskIdsFiles() {
SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
@@ -586,7 +586,7 @@
private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
int[] candidateUserIds;
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
// Remove only from directories of the users who have recents in memory synchronized
// with persistent storage.
candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
@@ -652,7 +652,7 @@
if (probablyDone) {
if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
persistentTaskIds.clear();
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
mRecentTasks.getPersistableTaskIds(persistentTaskIds);
mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
@@ -736,7 +736,7 @@
StringWriter stringWriter = null;
TaskRecord task = ((TaskWriteQueueItem) item).mTask;
if (DEBUG) Slog.d(TAG, "Writing task=" + task);
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (task.inRecents) {
// Still there.
try {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0ea49b2..c8ffdb1 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -264,7 +264,7 @@
/** The process that had previously hosted the root activity of this task.
* Used to know that we should try harder to keep this process around, in case the
* user wants to return to it. */
- private ProcessRecord mRootProcess;
+ private WindowProcessController mRootProcess;
/** Takes on same value as first root activity */
boolean isPersistable = false;
@@ -295,7 +295,7 @@
int mCallingUid;
String mCallingPackage;
- final ActivityManagerService mService;
+ final ActivityTaskManagerService mService;
private final Rect mTmpStableBounds = new Rect();
private final Rect mTmpNonDecorBounds = new Rect();
@@ -320,10 +320,10 @@
private TaskWindowContainerController mWindowContainerController;
/**
- * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
- * Intent, TaskDescription)} instead.
+ * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
+ * ActivityInfo, Intent, TaskDescription)} instead.
*/
- TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
+ TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
mService = service;
userId = UserHandle.getUserId(info.applicationInfo.uid);
@@ -339,14 +339,15 @@
setIntent(_intent, info);
setMinDimensions(info);
touchActiveTime();
- mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
+ mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
/**
- * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
+ * Don't use constructor directly.
+ * Use {@link #create(ActivityTaskManagerService, int, ActivityInfo,
* Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
*/
- TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
+ TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
TaskDescription _taskDescription) {
mService = service;
userId = UserHandle.getUserId(info.applicationInfo.uid);
@@ -369,13 +370,13 @@
lastTaskDescription = _taskDescription;
touchActiveTime();
- mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
+ mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
/**
* Don't use constructor directly. This is only used by XML parser.
*/
- TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
+ TaskRecord(ActivityTaskManagerService service, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
@@ -419,7 +420,7 @@
mSupportsPictureInPicture = supportsPictureInPicture;
mMinWidth = minWidth;
mMinHeight = minHeight;
- mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
+ mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
TaskWindowContainerController getWindowContainerController() {
@@ -460,13 +461,13 @@
// default configuration the next time it launches.
updateOverrideConfiguration(null);
}
- mService.mTaskChangeNotificationController.notifyTaskRemoved(taskId);
+ mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
mWindowContainerController = null;
}
@Override
public void onSnapshotChanged(TaskSnapshot snapshot) {
- mService.mTaskChangeNotificationController.notifyTaskSnapshotChanged(taskId, snapshot);
+ mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
}
void setResizeMode(int resizeMode) {
@@ -486,7 +487,7 @@
// TODO: Consolidate this with the resize() method below.
@Override
public void requestResize(Rect bounds, int resizeMode) {
- mService.mActivityTaskManager.resizeTask(taskId, bounds, resizeMode);
+ mService.resizeTask(taskId, bounds, resizeMode);
}
boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
@@ -1130,7 +1131,7 @@
// activity
reportOut.numRunning = 0;
}
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
// Increment the number of actually running activities
reportOut.numRunning++;
}
@@ -1302,7 +1303,7 @@
// We normally notify listeners of task stack changes on pause, however pinned stack
// activities are normally in the paused state so no notification will be sent there
// before the activity is removed. We send it here so instead.
- mService.mTaskChangeNotificationController.notifyTaskStackChanged();
+ mService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
if (mActivities.isEmpty()) {
@@ -1908,18 +1909,18 @@
}
}
- void setRootProcess(ProcessRecord proc) {
+ void setRootProcess(WindowProcessController proc) {
clearRootProcess();
if (intent != null &&
(intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
mRootProcess = proc;
- proc.recentTasks.add(this);
+ mRootProcess.addRecentTask(this);
}
}
void clearRootProcess() {
if (mRootProcess != null) {
- mRootProcess.recentTasks.remove(this);
+ mRootProcess.removeRecentTask(this);
mRootProcess = null;
}
}
@@ -2205,14 +2206,14 @@
sTaskRecordFactory = factory;
}
- static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+ static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent, IVoiceInteractionSession voiceSession,
IVoiceInteractor voiceInteractor) {
return getTaskRecordFactory().create(
service, taskId, info, intent, voiceSession, voiceInteractor);
}
- static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+ static TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent, TaskDescription taskDescription) {
return getTaskRecordFactory().create(service, taskId, info, intent, taskDescription);
}
@@ -2229,14 +2230,14 @@
*/
static class TaskRecordFactory {
- TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+ TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent, IVoiceInteractionSession voiceSession,
IVoiceInteractor voiceInteractor) {
return new TaskRecord(
service, taskId, info, intent, voiceSession, voiceInteractor);
}
- TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+ TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent, TaskDescription taskDescription) {
return new TaskRecord(service, taskId, info, intent, taskDescription);
}
@@ -2244,7 +2245,7 @@
/**
* Should only be used when we're restoring {@link TaskRecord} from storage.
*/
- TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+ TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
Intent affinityIntent, String affinity, String rootAffinity,
ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
boolean autoRemoveRecents, boolean askedCompatMode, int userId,
@@ -2468,7 +2469,8 @@
}
}
- final TaskRecord task = create(stackSupervisor.mService, taskId, intent, affinityIntent,
+ final TaskRecord task = create(stackSupervisor.mService,
+ taskId, intent, affinityIntent,
affinity, rootAffinity, realActivity, origActivity, rootHasReset,
autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 18c0957..992179a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -28,9 +28,9 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
-import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
-import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.UserState.STATE_BOOTING;
import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
@@ -41,6 +41,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
@@ -80,7 +81,6 @@
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.IntArray;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -88,7 +88,6 @@
import android.util.TimingsTraceLog;
import android.util.proto.ProtoOutputStream;
-import android.view.Window;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -2097,9 +2096,7 @@
return mService.mWindowManager;
}
void activityManagerOnUserStopped(int userId) {
- synchronized (mService) {
- mService.onUserStoppedLocked(userId);
- }
+ LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
}
void systemServiceManagerCleanupUser(int userId) {
@@ -2174,9 +2171,7 @@
}
void updateUserConfiguration() {
- synchronized (mService) {
- mService.updateUserConfigurationLocked();
- }
+ mService.mActivityTaskManager.updateUserConfiguration();
}
void clearBroadcastQueueForUser(int userId) {
@@ -2187,7 +2182,7 @@
void loadUserRecents(int userId) {
synchronized (mService) {
- mService.getRecentTasks().loadUserRecentsLocked(userId);
+ mService.mActivityTaskManager.getRecentTasks().loadUserRecentsLocked(userId);
}
}
@@ -2248,12 +2243,12 @@
protected void clearAllLockedTasks(String reason) {
synchronized (mService) {
- mService.getLockTaskController().clearLockedTasks(reason);
+ mService.mActivityTaskManager.getLockTaskController().clearLockedTasks(reason);
}
}
protected boolean isCallerRecents(int callingUid) {
- return mService.getRecentTasks().isCallerRecents(callingUid);
+ return mService.mActivityTaskManager.getRecentTasks().isCallerRecents(callingUid);
}
}
}
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 45410d7..366f95a 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -147,14 +147,15 @@
*
* <p>Note: This must be called with the global ActivityManagerService lock held.
*
- * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling
- * group.
+ * @param proc is the WindowProcessController of the process that entered or left the TOP_APP
+ * scheduling group.
*/
- public void onTopProcChangedLocked(ProcessRecord proc) {
- if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
- setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true);
+ public void onTopProcChangedLocked(WindowProcessController proc) {
+ final int curSchedGroup = proc.getCurrentSchedulingGroup();
+ if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ setVrRenderThreadLocked(proc.mVrThreadTid, curSchedGroup, true);
} else {
- if (proc.vrThreadTid == mVrRenderThreadTid) {
+ if (proc.mVrThreadTid == mVrRenderThreadTid) {
clearVrRenderThreadLocked(true);
}
}
@@ -190,7 +191,7 @@
changed = changeVrModeLocked(vrMode, record.app);
if (record.app != null) {
- processId = record.app.pid;
+ processId = record.app.getPid();
}
}
@@ -213,9 +214,9 @@
*
* @param tid the tid of the thread to set, or 0 to unset the current thread.
* @param pid the pid of the process owning the thread to set.
- * @param proc the ProcessRecord of the process owning the thread to set.
+ * @param proc the WindowProcessController of the process owning the thread to set.
*/
- public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+ public void setVrThreadLocked(int tid, int pid, WindowProcessController proc) {
if (hasPersistentVrFlagSet()) {
Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
return;
@@ -230,9 +231,9 @@
if (!inVrMode()) {
Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
} else {
- setVrRenderThreadLocked(tid, proc.curSchedGroup, false);
+ setVrRenderThreadLocked(tid, proc.getCurrentSchedulingGroup(), false);
}
- proc.vrThreadTid = (tid > 0) ? tid : 0;
+ proc.mVrThreadTid = (tid > 0) ? tid : 0;
}
/**
@@ -280,11 +281,11 @@
* <p>Note: This must be called with the global ActivityManagerService lock held.
*
* @param vrMode {@code true} if the system VR mode is being enabled.
- * @param proc the ProcessRecord of the process enabling the system VR mode.
+ * @param proc the WindowProcessController of the process enabling the system VR mode.
*
* @return {@code true} if our state changed.
*/
- private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) {
+ private boolean changeVrModeLocked(boolean vrMode, WindowProcessController proc) {
final int oldVrState = mVrState;
// This is the only place where mVrState should have its FLAG_VR_MODE setting
@@ -299,8 +300,9 @@
if (changed) {
if (proc != null) {
- if (proc.vrThreadTid > 0) {
- setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false);
+ if (proc.mVrThreadTid > 0) {
+ setVrRenderThreadLocked(
+ proc.mVrThreadTid, proc.getCurrentSchedulingGroup(), false);
}
} else {
clearVrRenderThreadLocked(false);
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
new file mode 100644
index 0000000..64a273e
--- /dev/null
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -0,0 +1,526 @@
+/*
+ * 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.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+
+import android.app.Activity;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledRunnable;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The Activity Manager (AM) package manages the lifecycle of processes in the system through
+ * {@link ProcessRecord}. However, it is important for the Window Manager (WM) package to be aware
+ * of the processes and their state since it affects how WM manages windows and activities. This
+ * class that allows the {@link ProcessRecord} object in the AM package to communicate important
+ * changes to its state to the WM package in a structured way. WM package also uses
+ * {@link WindowProcessListener} to request changes to the process state on the AM side.
+ * Note that public calls into this class are assumed to be originating from outside the
+ * window manager so the window manager lock is held and appropriate permissions are checked before
+ * calls are allowed to proceed.
+ */
+public class WindowProcessController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
+ private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+
+ // all about the first app in the process
+ final ApplicationInfo mInfo;
+ final String mName;
+ final int mUid;
+ // The process of this application; 0 if none
+ private volatile int mPid;
+ // user of process.
+ final int mUserId;
+ // The owner of this window process controller object. Mainly for identification when we
+ // communicate back to the activity manager side.
+ public final Object mOwner;
+ // List of packages running in the process
+ final ArraySet<String> mPkgList = new ArraySet<>();
+ private final WindowProcessListener mListener;
+ 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;
+ // Currently desired scheduling class
+ private volatile int mCurSchedGroup;
+ // Last reported process state;
+ private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
+ // are we in the process of crashing?
+ private volatile boolean mCrashing;
+ // does the app have a not responding dialog?
+ private volatile boolean mNotResponding;
+ // always keep this application running?
+ private volatile boolean mPersistent;
+ // The ABI this process was launched with
+ private volatile String mRequiredAbi;
+ // Running any services that are foreground?
+ private volatile boolean mHasForegroundServices;
+
+ // Thread currently set for VR scheduling
+ int mVrThreadTid;
+
+ // all activities running in the process
+ private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
+ // any tasks this process had run root activities in
+ private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
+
+ WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
+ int uid, int userId, Object owner, WindowProcessListener listener) {
+ mInfo = info;
+ mName = name;
+ mUid = uid;
+ mUserId = userId;
+ mOwner = owner;
+ mListener = listener;
+ mAtm = atm;
+ }
+
+ public void setPid(int pid) {
+ mPid = pid;
+ }
+
+ int getPid() {
+ return mPid;
+ }
+
+ public void setThread(IApplicationThread thread) {
+ mThread = thread;
+ }
+
+ IApplicationThread getThread() {
+ return mThread;
+ }
+
+ boolean hasThread() {
+ return mThread != null;
+ }
+
+ public void setCurrentSchedulingGroup(int curSchedGroup) {
+ mCurSchedGroup = curSchedGroup;
+ }
+
+ int getCurrentSchedulingGroup() {
+ return mCurSchedGroup;
+ }
+
+ public void setReportedProcState(int repProcState) {
+ mRepProcState = repProcState;
+ }
+
+ int getReportedProcState() {
+ return mRepProcState;
+ }
+
+ public void setCrashing(boolean crashing) {
+ mCrashing = crashing;
+ }
+
+ boolean isCrashing() {
+ return mCrashing;
+ }
+
+ public void setNotResponding(boolean notResponding) {
+ mNotResponding = notResponding;
+ }
+
+ boolean isNotResponding() {
+ return mNotResponding;
+ }
+
+ public void setPersistent(boolean persistent) {
+ mPersistent = persistent;
+ }
+
+ boolean isPersistent() {
+ return mPersistent;
+ }
+
+ public void setHasForegroundServices(boolean hasForegroundServices) {
+ mHasForegroundServices = hasForegroundServices;
+ }
+
+ boolean hasForegroundServices() {
+ return mHasForegroundServices;
+ }
+
+ public void setRequiredAbi(String requiredAbi) {
+ mRequiredAbi = requiredAbi;
+ }
+
+ String getRequiredAbi() {
+ return mRequiredAbi;
+ }
+
+ public void addPackage(String packageName) {
+ synchronized (mAtm.mGlobalLock) {
+ mPkgList.add(packageName);
+ }
+ }
+
+ public void clearPackageList() {
+ synchronized (mAtm.mGlobalLock) {
+ mPkgList.clear();
+ }
+ }
+
+ void addActivityIfNeeded(ActivityRecord r) {
+ if (mActivities.contains(r)) {
+ return;
+ }
+ mActivities.add(r);
+ }
+
+ void removeActivity(ActivityRecord r) {
+ mActivities.remove(r);
+ }
+
+ public void clearActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ mActivities.clear();
+ }
+ }
+
+ public boolean hasActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mActivities.isEmpty();
+ }
+ }
+
+ public boolean hasVisibleActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.visible) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasActivitiesOrRecentTasks() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
+ }
+ }
+
+ public void stopFreezingActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ int i = mActivities.size();
+ while (i > 0) {
+ i--;
+ mActivities.get(i).stopFreezingScreenLocked(true);
+ }
+ }
+ }
+
+ public void finishActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
+ for (int i = 0; i < activities.size(); i++) {
+ final ActivityRecord r = activities.get(i);
+ if (!r.finishing && r.isInStackLocked()) {
+ r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+ null, "finish-heavy", true);
+ }
+ }
+ }
+ }
+
+ public boolean isInterestingToUser() {
+ synchronized (mAtm.mGlobalLock) {
+ final int size = mActivities.size();
+ for (int i = 0; i < size; i++) {
+ ActivityRecord r = mActivities.get(i);
+ if (r.isInterestingToUserLocked()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasRunningActivity(String packageName) {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (packageName.equals(r.packageName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void clearPackagePreferredForHomeActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.isActivityTypeHome()) {
+ Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+ try {
+ ActivityThread.getPackageManager()
+ .clearPackagePreferredActivities(r.packageName);
+ } catch (RemoteException c) {
+ // pm is in same process, this will never happen.
+ }
+ }
+ }
+ }
+ }
+
+ boolean hasStartedActivity(ActivityRecord launchedActivity) {
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord activity = mActivities.get(i);
+ if (launchedActivity == activity) {
+ continue;
+ }
+ if (!activity.stopped) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public void updateIntentForHeavyWeightActivity(Intent intent) {
+ synchronized (mAtm.mGlobalLock) {
+ if (mActivities.isEmpty()) {
+ return;
+ }
+ ActivityRecord hist = mActivities.get(0);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+ }
+ }
+
+ boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
+ for (int k = 0; k < mActivities.size(); k++) {
+ final TaskRecord otherTask = mActivities.get(k).getTask();
+ if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+ // Don't kill process(es) that has an activity in a different task that is
+ // also in recents.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ArraySet<TaskRecord> getReleaseSomeActivitiesTasks() {
+ // Examine all activities currently running in the process.
+ TaskRecord firstTask = null;
+ // Tasks is non-null only if two or more tasks are found.
+ ArraySet<TaskRecord> tasks = null;
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
+ for (int i = 0; i < mActivities.size(); i++) {
+ final ActivityRecord r = mActivities.get(i);
+ // First, if we find an activity that is in the process of being destroyed,
+ // then we just aren't going to do anything for now; we want things to settle
+ // down before we try to prune more activities.
+ if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
+ return null;
+ }
+ // Don't consider any activies that are currently not in a state where they
+ // can be destroyed.
+ if (r.visible || !r.stopped || !r.haveState
+ || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
+ continue;
+ }
+
+ final TaskRecord task = r.getTask();
+ if (task != null) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
+ + " from " + r);
+ if (firstTask == null) {
+ firstTask = task;
+ } else if (firstTask != task) {
+ if (tasks == null) {
+ tasks = new ArraySet<>();
+ tasks.add(firstTask);
+ }
+ tasks.add(task);
+ }
+ }
+ }
+
+ return tasks;
+ }
+
+ public interface ComputeOomAdjCallback {
+ void onVisibleActivity();
+ void onPausedActivity();
+ void onStoppingActivity(boolean finishing);
+ void onOtherActivity();
+ }
+
+ public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+ synchronized (mAtm.mGlobalLock) {
+ final int activitiesSize = mActivities.size();
+ for (int j = 0; j < activitiesSize; j++) {
+ final ActivityRecord r = mActivities.get(j);
+ if (r.app != this) {
+ Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ + " instead of expected " + this);
+ if (r.app == null || (r.app.mUid == mUid)) {
+ // Only fix things up when they look sane
+ r.setProcess(this);
+ } else {
+ continue;
+ }
+ }
+ if (r.visible) {
+ callback.onVisibleActivity();
+ final TaskRecord task = r.getTask();
+ if (task != null && minTaskLayer > 0) {
+ final int layer = task.mLayerRank;
+ if (layer >= 0 && minTaskLayer > layer) {
+ minTaskLayer = layer;
+ }
+ }
+ break;
+ } else if (r.isState(PAUSING, PAUSED)) {
+ callback.onPausedActivity();
+ } else if (r.isState(STOPPING)) {
+ callback.onStoppingActivity(r.finishing);
+ } else {
+ callback.onOtherActivity();
+ }
+ }
+ }
+
+ return minTaskLayer;
+ }
+
+ void clearProfilerIfNeeded() {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ mAtm.mH.post(() -> mListener.clearProfilerIfNeeded());
+ }
+
+ void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+ boolean activityChange, boolean updateOomAdj) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(WindowProcessListener::updateProcessInfo,
+ mListener, updateServiceConnectionActivities, updateLru, activityChange,
+ updateOomAdj);
+ mAtm.mH.post(r);
+ }
+
+ void updateServiceConnectionActivities() {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ mAtm.mH.post(() -> mListener.updateServiceConnectionActivities());
+ }
+
+ void setPendingUiClean(boolean pendingUiClean) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
+ mAtm.mH.post(r);
+ }
+
+ void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
+ mListener, newState);
+ mAtm.mH.post(r);
+ }
+
+ void setRemoved(boolean removed) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ WindowProcessListener::setRemoved, mListener, removed);
+ mAtm.mH.post(r);
+ }
+
+ void addRecentTask(TaskRecord task) {
+ mRecentTasks.add(task);
+ }
+
+ void removeRecentTask(TaskRecord task) {
+ mRecentTasks.remove(task);
+ }
+
+ public boolean hasRecentTasks() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mRecentTasks.isEmpty();
+ }
+ }
+
+ public void clearRecentTasks() {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ mRecentTasks.get(i).clearRootProcess();
+ }
+ mRecentTasks.clear();
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mAtm.mGlobalLock) {
+ if (mActivities.size() > 0) {
+ pw.print(prefix); pw.println("Activities:");
+ for (int i = 0; i < mActivities.size(); i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mActivities.get(i));
+ }
+ }
+
+ if (mRecentTasks.size() > 0) {
+ pw.println(prefix + "Recent Tasks:");
+ for (int i = 0; i < mRecentTasks.size(); i++) {
+ pw.println(prefix + " - " + mRecentTasks.get(i));
+ }
+ }
+
+ if (mVrThreadTid != 0) {
+ pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
new file mode 100644
index 0000000..92e4461
--- /dev/null
+++ b/services/core/java/com/android/server/am/WindowProcessListener.java
@@ -0,0 +1,47 @@
+/*
+ * 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.am;
+
+/**
+ * Interface used by the owner/creator of a process that owns windows to listen to changes from the
+ * WM side.
+ * @see WindowProcessController
+ */
+public interface WindowProcessListener {
+
+ /** Clear the profiler record if we are currently profiling this process. */
+ void clearProfilerIfNeeded();
+
+ /** Update the service connection for this process based on activities it might have. */
+ void updateServiceConnectionActivities();
+
+ /** Set or clear flag that we would like to clean-up UI resources for this process. */
+ void setPendingUiClean(boolean pendingUiClean);
+
+ /**
+ * Set flag that we would like to clean-up UI resources for this process and force new process
+ * state.
+ */
+ void setPendingUiCleanAndForceProcessStateUpTo(int newState);
+
+ /** Update the process information. */
+ void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+ boolean activityChange, boolean updateOomAdj);
+
+ /** Set process package been removed from device. */
+ void setRemoved(boolean removed);
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2a5249b..6971f71 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -20,8 +20,6 @@
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static android.media.AudioManager.STREAM_ALARM;
-import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioManager.STREAM_SYSTEM;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
@@ -141,6 +139,7 @@
import com.android.server.audio.AudioServiceEvents.VolumeEvent;
import com.android.server.audio.AudioServiceEvents.WiredDevConnectEvent;
import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParserException;
@@ -236,7 +235,6 @@
private static final int MSG_PERSIST_RINGER_MODE = 3;
private static final int MSG_AUDIO_SERVER_DIED = 4;
private static final int MSG_PLAY_SOUND_EFFECT = 5;
- private static final int MSG_BTA2DP_DOCK_TIMEOUT = 6;
private static final int MSG_LOAD_SOUND_EFFECTS = 7;
private static final int MSG_SET_FORCE_USE = 8;
private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
@@ -268,6 +266,7 @@
private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
private static final int MSG_DISABLE_AUDIO_FOR_UID = 104;
private static final int MSG_SET_HEARING_AID_CONNECTION_STATE = 105;
+ private static final int MSG_BTA2DP_DOCK_TIMEOUT = 106;
// end of messages handled under wakelock
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -4525,13 +4524,21 @@
}
synchronized (mLastDeviceConnectMsgTime) {
long time = SystemClock.uptimeMillis() + delay;
- handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
- if (msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
- msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
- msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||
- msg == MSG_SET_HEARING_AID_CONNECTION_STATE) {
+
+ if (msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
+ msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||
+ msg == MSG_SET_HEARING_AID_CONNECTION_STATE ||
+ msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
+ msg == MSG_A2DP_DEVICE_CONFIG_CHANGE ||
+ msg == MSG_BTA2DP_DOCK_TIMEOUT) {
+ if (mLastDeviceConnectMsgTime >= time) {
+ // add a little delay to make sure messages are ordered as expected
+ time = mLastDeviceConnectMsgTime + 30;
+ }
mLastDeviceConnectMsgTime = time;
}
+
+ handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
}
}
@@ -4693,6 +4700,13 @@
} else {
delay = 0;
}
+
+ if (DEBUG_DEVICES) {
+ Log.d(TAG, "setBluetoothA2dpDeviceConnectionStateInt device: " + device
+ + " state: " + state + " delay(ms): " + delay
+ + " suppressNoisyIntent: " + suppressNoisyIntent);
+ }
+
queueMsgUnderWakeLock(mAudioHandler,
(profile == BluetoothProfile.A2DP ?
MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
@@ -5601,6 +5615,7 @@
synchronized (mConnectedDevices) {
makeA2dpDeviceUnavailableNow( (String) msg.obj );
}
+ mAudioEventWakeLock.release();
break;
case MSG_SET_FORCE_USE:
@@ -5831,6 +5846,9 @@
// must be called synchronized on mConnectedDevices
private void makeA2dpDeviceUnavailableNow(String address) {
+ if (address == null) {
+ return;
+ }
synchronized (mA2dpAvrcpLock) {
mAvrcpAbsVolSupported = false;
}
@@ -5840,6 +5858,9 @@
makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
// Remove A2DP routes as well
setCurrentAudioRouteName(null);
+ if (mDockAddress == address) {
+ mDockAddress = null;
+ }
}
// must be called synchronized on mConnectedDevices
@@ -5851,9 +5872,12 @@
mConnectedDevices.remove(
makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
// send the delayed message to make the device unavailable later
- Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
- mAudioHandler.sendMessageDelayed(msg, delayMs);
-
+ queueMsgUnderWakeLock(mAudioHandler,
+ MSG_BTA2DP_DOCK_TIMEOUT,
+ 0,
+ 0,
+ address,
+ delayMs);
}
// must be called synchronized on mConnectedDevices
@@ -5925,7 +5949,8 @@
private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state, int a2dpVolume)
{
if (DEBUG_DEVICES) {
- Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice+"state=" + state);
+ Log.d(TAG, "onSetA2dpSinkConnectionState btDevice= " + btDevice+" state= " + state
+ + " is dock: "+btDevice.isBluetoothDock());
}
if (btDevice == null) {
return;
@@ -5962,7 +5987,7 @@
} else {
// this could be a connection of another A2DP device before the timeout of
// a dock: cancel the dock timeout, and make the dock unavailable now
- if(hasScheduledA2dpDockTimeout()) {
+ if (hasScheduledA2dpDockTimeout() && mDockAddress != null) {
cancelA2dpDeviceTimeout();
makeA2dpDeviceUnavailableNow(mDockAddress);
}
@@ -6181,17 +6206,6 @@
}
}
- if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) ||
- mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) ||
- mAudioHandler.hasMessages(MSG_SET_HEARING_AID_CONNECTION_STATE) ||
- mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
- synchronized (mLastDeviceConnectMsgTime) {
- long time = SystemClock.uptimeMillis();
- if (mLastDeviceConnectMsgTime > time) {
- delay = (int)(mLastDeviceConnectMsgTime - time) + 30;
- }
- }
- }
return delay;
}
@@ -6642,7 +6656,8 @@
// when the user switches back. For managed profiles, we should kill all recording apps
ComponentName homeActivityName = null;
if (!oldUser.isManagedProfile()) {
- homeActivityName = mActivityManagerInternal.getHomeActivityForUser(oldUser.id);
+ homeActivityName = LocalServices.getService(
+ ActivityTaskManagerInternal.class).getHomeActivityForUser(oldUser.id);
}
final String[] permissions = { Manifest.permission.RECORD_AUDIO };
List<PackageInfo> packages;
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
similarity index 72%
rename from services/core/java/com/android/server/fingerprint/AuthenticationClient.java
rename to services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
index afd1a94..1ed2847 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,25 +11,22 @@
* 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.
+ * limitations under the License
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.common;
import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
/**
@@ -51,6 +48,7 @@
private Bundle mBundle;
private IStatusBarService mStatusBarService;
private boolean mInLockout;
+ // TODO: BiometricManager, after other biometric modalities are introduced.
private final FingerprintManager mFingerprintManager;
protected boolean mDialogDismissed;
@@ -62,12 +60,12 @@
try {
mDialogReceiverFromClient.onDialogDismissed(reason);
if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
+ onError(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
0 /* vendorCode */);
}
mDialogDismissed = true;
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify dialog dismissed", e);
+ Slog.e(getLogTag(), "Unable to notify dialog dismissed", e);
}
stop(true /* initiatedByClient */);
}
@@ -85,11 +83,13 @@
*/
public abstract void onStop();
- public AuthenticationClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
+ public AuthenticationClient(Context context, Metrics metrics,
+ BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
+ BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
- super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
+ super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
+ restricted, owner);
mOpId = opId;
mBundle = bundle;
mDialogReceiverFromClient = dialogReceiver;
@@ -112,17 +112,17 @@
// If the dialog is showing, the client doesn't need to receive onAcquired messages.
if (mBundle != null) {
try {
- if (acquiredInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
mStatusBarService.onFingerprintHelp(
mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
}
return false; // acquisition continues
} catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending acquired message", e);
+ Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
return true; // client failed
} finally {
// Good scans will keep the device awake
- if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
notifyUserActivity();
}
}
@@ -145,7 +145,7 @@
mStatusBarService.onFingerprintError(
mFingerprintManager.getErrorString(error, vendorCode));
} catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending error", e);
+ Slog.e(getLogTag(), "Remote exception when sending error", e);
}
}
return super.onError(error, vendorCode);
@@ -166,36 +166,35 @@
com.android.internal.R.string.fingerprint_not_recognized));
}
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to notify Authenticated:", e);
+ Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
}
}
- IFingerprintServiceReceiver receiver = getReceiver();
- if (receiver != null) {
+ final BiometricService.ServiceListener listener = getListener();
+ if (listener != null) {
try {
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
- authenticated);
+ mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
if (!authenticated) {
- receiver.onAuthenticationFailed(getHalDeviceId());
+ listener.onAuthenticationFailed(getHalDeviceId());
} else {
if (DEBUG) {
- Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
+ Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
+ ", id=" + fingerId + ", gp=" + groupId + ")");
}
Fingerprint fp = !getIsRestricted()
? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
: null;
- receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
+ listener.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
}
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify Authenticated:", e);
+ Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
result = true; // client failed
}
} else {
result = true; // client not listening
}
if (!authenticated) {
- if (receiver != null) {
+ if (listener != null) {
vibrateError();
}
// allow system-defined limit of number of attempts before giving up
@@ -203,17 +202,17 @@
if (lockoutMode != LOCKOUT_NONE) {
try {
mInLockout = true;
- Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
+ Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
lockoutMode + ")");
stop(false);
int errorCode = lockoutMode == LOCKOUT_TIMED ?
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
// TODO: if the dialog is showing, this error should be delayed. On a similar
// note, AuthenticationClient should override onError and delay all other errors
// as well, if the dialog is showing
- receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+ listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
// Send the lockout message to the system dialog
if (mBundle != null) {
@@ -221,12 +220,12 @@
mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
}
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify lockout:", e);
+ Slog.w(getLogTag(), "Failed to notify lockout:", e);
}
}
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
} else {
- if (receiver != null) {
+ if (listener != null) {
vibrateSuccess();
}
result |= true; // we have a valid fingerprint, done
@@ -241,32 +240,27 @@
*/
@Override
public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "start authentication: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
onStart();
try {
- final int result = daemon.authenticate(mOpId, getGroupId());
+ final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
if (result != 0) {
- Slog.w(TAG, "startAuthentication failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
+ mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result);
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
return result;
}
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+ if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
// If authenticating with system dialog, show the dialog
if (mBundle != null) {
try {
mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to show fingerprint dialog", e);
+ Slog.e(getLogTag(), "Unable to show fingerprint dialog", e);
}
}
} catch (RemoteException e) {
- Slog.e(TAG, "startAuthentication failed", e);
+ Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
@@ -275,25 +269,21 @@
@Override
public int stop(boolean initiatedByClient) {
if (mAlreadyCancelled) {
- Slog.w(TAG, "stopAuthentication: already cancelled!");
+ Slog.w(getLogTag(), "stopAuthentication: already cancelled!");
return 0;
}
onStop();
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
+
try {
- final int result = daemon.cancel();
+ final int result = getDaemonWrapper().cancel();
if (result != 0) {
- Slog.w(TAG, "stopAuthentication failed, result=" + result);
+ Slog.w(getLogTag(), "stopAuthentication failed, result=" + result);
return result;
}
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
+ if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer authenticating");
} catch (RemoteException e) {
- Slog.e(TAG, "stopAuthentication failed", e);
+ Slog.e(getLogTag(), "stopAuthentication failed", e);
return ERROR_ESRCH;
} finally {
// If the user already cancelled authentication (via some interaction with the
@@ -304,29 +294,30 @@
try {
mStatusBarService.hideFingerprintDialog();
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to hide fingerprint dialog", e);
+ Slog.e(getLogTag(), "Unable to hide fingerprint dialog", e);
}
}
}
+
mAlreadyCancelled = true;
return 0; // success
}
@Override
public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
+ if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for authenticate!");
return true; // Invalid for Authenticate
}
@Override
public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
+ if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for authenticate!");
return true; // Invalid for Authenticate
}
@Override
public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
+ if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for authenticate!");
return true; // Invalid for Authenticate
}
}
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricService.java b/services/core/java/com/android/server/biometrics/common/BiometricService.java
new file mode 100644
index 0000000..7d80955
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricService.java
@@ -0,0 +1,973 @@
+/*
+ * 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.biometrics.common;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.KeyStore;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.SystemService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Abstract base class containing all of the business logic for biometric services, e.g.
+ * Fingerprint, Face, Iris.
+ *
+ * @hide
+ */
+public abstract class BiometricService extends SystemService implements IHwBinder.DeathRecipient {
+
+ protected static final boolean DEBUG = true;
+
+ private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
+ private static final int MSG_USER_SWITCHING = 10;
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+ private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
+
+ private final Context mContext;
+ private final String mKeyguardPackage;
+ private final AppOpsManager mAppOps;
+ private final SparseBooleanArray mTimedLockoutCleared;
+ private final SparseIntArray mFailedAttempts;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final AlarmManager mAlarmManager;
+ private final PowerManager mPowerManager;
+ private final UserManager mUserManager;
+ private final MetricsLogger mMetricsLogger;
+ private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
+ private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
+ private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+
+ protected final ResetFailedAttemptsForUserRunnablle mResetFailedAttemptsForCurrentUserRunnable =
+ new ResetFailedAttemptsForUserRunnablle();
+ protected final H mHandler = new H();
+
+ private ClientMonitor mCurrentClient;
+ private ClientMonitor mPendingClient;
+ private PerformanceStats mPerformanceStats;
+ protected int mCurrentUserId = UserHandle.USER_NULL;
+ // Normal authentications are tracked by mPerformanceMap.
+ protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
+ // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+ protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+
+ protected class PerformanceStats {
+ public int accept; // number of accepted biometrics
+ public int reject; // number of rejected biometrics
+ public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
+ // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+ public int lockout; // total number of lockouts
+ public int permanentLockout; // total number of permanent lockouts
+ }
+
+ /**
+ * @return the log tag.
+ */
+ protected abstract String getTag();
+
+ /**
+ * @return the number of failed attempts after which the user will be temporarily locked out
+ * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+ */
+ protected abstract int getFailedAttemptsLockoutTimed();
+
+ /**
+ * @return the number of failed attempts after which the user will be permanently locked out
+ * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+ */
+ protected abstract int getFailedAttemptsLockoutPermanent();
+
+ /**
+ * @return the metrics constants for a biometric implementation.
+ */
+ protected abstract Metrics getMetrics();
+
+ /**
+ * @param userId
+ * @return true if the enrollment limit has been reached.
+ */
+ protected abstract boolean hasReachedEnrollmentLimit(int userId);
+
+ /**
+ * Notifies the HAL that the user has changed.
+ * @param userId
+ * @param clientPackage
+ */
+ protected abstract void updateActiveGroup(int userId, String clientPackage);
+
+ /**
+ * @return The protected intent to reset lockout for a specific biometric.
+ */
+ protected abstract String getLockoutResetIntent();
+
+ /**
+ * @return The permission the sender is required to have in order for the lockout reset intent
+ * to be received by the BiometricService implementation.
+ */
+ protected abstract String getLockoutBroadcastPermission();
+
+ /**
+ * @return The HAL ID.
+ */
+ protected abstract long getHalDeviceId();
+
+ /**
+ * This method is called when the user switches. Implementations should probably notify the
+ * HAL.
+ * @param userId
+ */
+ protected abstract void handleUserSwitching(int userId);
+
+ /**
+ * @param userId
+ * @return Returns true if the user has any enrolled biometrics.
+ */
+ protected abstract boolean hasEnrolledBiometrics(int userId);
+
+ /**
+ * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
+ * etc.
+ */
+ protected abstract String getManageBiometricPermission();
+
+ /**
+ * Checks if the caller has permission to use the biometric service - throws a SecurityException
+ * if not.
+ */
+ protected abstract void checkUseBiometricPermission();
+
+ /**
+ * @return Returns one of the {@link AppOpsManager} constants which pertains to the specific
+ * biometric service.
+ */
+ protected abstract int getAppOp();
+
+ /**
+ * Notifies clients that lockout has been reset.
+ */
+ protected abstract void notifyLockoutResetMonitors();
+
+ /**
+ * Notifies clients of any change in the biometric state (active / idle). This is mainly for
+ * Fingerprint navigation gestures.
+ * @param isActive
+ */
+ protected void notifyClientActiveCallbacks(boolean isActive) {}
+
+ protected class AuthenticationClientImpl extends AuthenticationClient {
+
+ public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
+ boolean restricted, String owner, Bundle bundle,
+ IBiometricPromptReceiver dialogReceiver,
+ IStatusBarService statusBarService) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener,
+ targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
+ statusBarService);
+ }
+
+ @Override
+ public void onStart() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Could not register task stack listener", e);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Could not unregister task stack listener", e);
+ }
+ }
+
+ @Override
+ public void resetFailedAttempts() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+
+ @Override
+ public int handleFailedAttempt() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+ mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+ final int lockoutMode = getLockoutMode();
+ if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+ mPerformanceStats.permanentLockout++;
+ } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+ mPerformanceStats.lockout++;
+ }
+
+ // Failing multiple times will continue to push out the lockout time
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ scheduleLockoutResetForUser(currentUser);
+ return lockoutMode;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+ }
+
+ protected class EnrollClientImpl extends EnrollClient {
+
+ public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int userId, int groupId,
+ byte[] cryptoToken, boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener,
+ userId, groupId, cryptoToken, restricted, owner);
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+ }
+
+ protected class RemovalClientImpl extends RemovalClient {
+ private boolean mShouldNotify;
+
+ public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+ boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+ userId, restricted, owner);
+ }
+
+ public void setShouldNotifyUserActivity(boolean shouldNotify) {
+ mShouldNotify = shouldNotify;
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ if (mShouldNotify) {
+ userActivity();
+ }
+ }
+ }
+
+ protected class EnumerateClientImpl extends EnumerateClient {
+
+ public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int groupId, int userId,
+ boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
+ restricted, owner);
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+ }
+
+ /**
+ * Wraps the callback interface from Service -> Manager
+ */
+ protected interface ServiceListener {
+ void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining)
+ throws RemoteException;
+
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException;
+
+ void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.BiometricIdentifier biometric, int userId)
+ throws RemoteException;
+
+ void onAuthenticationFailed(long deviceId)
+ throws RemoteException;
+
+ void onError(long deviceId, int error, int vendorCode)
+ throws RemoteException;
+
+ void onRemoved(long deviceId, int fingerId, int groupId, int remaining)
+ throws RemoteException;
+
+ void onEnumerated(long deviceId, int fingerId, int groupId, int remaining)
+ throws RemoteException;
+ }
+
+ /**
+ * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
+ * subclasses.
+ */
+ protected interface DaemonWrapper {
+ int authenticate(long operationId, int groupId) throws RemoteException;
+ int cancel() throws RemoteException;
+ int remove(int groupId, int biometricId) throws RemoteException;
+ int enumerate() throws RemoteException;
+ int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
+ }
+
+ /**
+ * Handler which all subclasses should post events to.
+ */
+ protected final class H extends Handler {
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_USER_SWITCHING:
+ handleUserSwitching(msg.arg1);
+ break;
+
+ default:
+ Slog.w(getTag(), "Unknown message:" + msg.what);
+ }
+ }
+ };
+
+ private final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ if (!(mCurrentClient instanceof AuthenticationClient)) {
+ return;
+ }
+ final String currentClient = mCurrentClient.getOwnerString();
+ if (isKeyguard(currentClient)) {
+ return; // Keyguard is always allowed
+ }
+ List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(currentClient)) {
+ Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
+ + " currentClient: " + currentClient);
+ mCurrentClient.stop(false /* initiatedByClient */);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to get running tasks", e);
+ }
+ }
+ };
+
+ private final class ResetClientStateRunnable implements Runnable {
+ @Override
+ public void run() {
+ /**
+ * Warning: if we get here, the driver never confirmed our call to cancel the current
+ * operation (authenticate, enroll, remove, enumerate, etc), which is
+ * really bad. The result will be a 3-second delay in starting each new client.
+ * If you see this on a device, make certain the driver notifies with
+ * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
+ * once it has successfully switched to the IDLE state in the HAL.
+ * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
+ * in response to an actual cancel() call.
+ */
+ Slog.w(getTag(), "Client "
+ + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+ + " failed to respond to cancel, starting client "
+ + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+
+ mCurrentClient = null;
+ startClient(mPendingClient, false);
+ }
+ };
+
+ private final class LockoutReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getLockoutResetIntent().equals(intent.getAction())) {
+ final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+ resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+ }
+ }
+ };
+
+ private final class ResetFailedAttemptsForUserRunnablle implements Runnable {
+ @Override
+ public void run() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+ };
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public BiometricService(Context context) {
+ super(context);
+ mContext = context;
+ mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mTimedLockoutCleared = new SparseBooleanArray();
+ mFailedAttempts = new SparseIntArray();
+ mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
+ Context.ACTIVITY_TASK_SERVICE)).getService();
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mUserManager = UserManager.get(mContext);
+ mMetricsLogger = new MetricsLogger();
+ mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+ getLockoutBroadcastPermission(), null /* handler */);
+ }
+
+ @Override
+ public void onStart() {
+ listenForUserSwitches();
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.e(getTag(), "HAL died");
+ mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+ handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /*vendorCode */);
+ }
+
+ protected ClientMonitor getCurrentClient() {
+ return mCurrentClient;
+ }
+
+ protected ClientMonitor getPendingClient() {
+ return mPendingClient;
+ }
+
+ /**
+ * Callback handlers from the daemon. The caller must put this on a handler.
+ */
+
+ protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
+ removeClient(client);
+ }
+ if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+ && client instanceof AuthenticationClient) {
+ // ignore enrollment acquisitions or acquisitions when we're locked out
+ mPerformanceStats.acquire++;
+ }
+ }
+
+ protected void handleAuthenticated(long deviceId, int biometricId, int groupId,
+ ArrayList<Byte> token) {
+ ClientMonitor client = mCurrentClient;
+ if (biometricId != 0) {
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
+ }
+ KeyStore.getInstance().addAuthToken(byteToken);
+ }
+ if (client != null && client.onAuthenticated(biometricId, groupId)) {
+ removeClient(client);
+ }
+ if (biometricId != 0) {
+ mPerformanceStats.accept++;
+ } else {
+ mPerformanceStats.reject++;
+ }
+ }
+
+ protected void handleEnrollResult(long deviceId, int biometricId, int groupId, int remaining) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onEnrollResult(biometricId, groupId, remaining)) {
+ removeClient(client);
+ // When enrollment finishes, update this group's authenticator id, as the HAL has
+ // already generated a new authenticator id when the new biometric is enrolled.
+ updateActiveGroup(groupId, null);
+ }
+ }
+
+ protected void handleError(long deviceId, int error, int vendorCode) {
+ final ClientMonitor client = mCurrentClient;
+
+ if (DEBUG) Slog.v(getTag(), "handleError(client="
+ + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+
+ if (client != null && client.onError(error, vendorCode)) {
+ removeClient(client);
+ }
+
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mHandler.removeCallbacks(mResetClientState);
+ if (mPendingClient != null) {
+ if (DEBUG) Slog.v(getTag(), "start pending client " + mPendingClient.getOwnerString());
+ startClient(mPendingClient, false);
+ mPendingClient = null;
+ }
+ }
+ }
+
+ protected void handleRemoved(final long deviceId,
+ final int biometricId,
+ final int groupId,
+ final int remaining) {
+ if (DEBUG) Slog.w(getTag(), "Removed: fid=" + biometricId
+ + ", gid=" + groupId
+ + ", dev=" + deviceId
+ + ", rem=" + remaining);
+
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onRemoved(biometricId, groupId, remaining)) {
+ removeClient(client);
+ // When the last biometric of a group is removed, update the authenticator id
+ if (!hasEnrolledBiometrics(groupId)) {
+ updateActiveGroup(groupId, null);
+ }
+ }
+ }
+
+ /**
+ * Calls from the Manager. These are still on the calling binder's thread.
+ */
+
+ protected void enrollInternal(EnrollClientImpl client, int userId) {
+ if (hasReachedEnrollmentLimit(userId)) {
+ return;
+ }
+
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default biometrics for the user.
+ if (!isCurrentUserOrProfile(userId)) {
+ return;
+ }
+
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ protected void cancelEnrollmentInternal(IBinder token) {
+ mHandler.post(() -> {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof EnrollClient && client.getToken() == token) {
+ client.stop(client.getToken() == token);
+ }
+ });
+ }
+
+ protected void authenticateInternal(AuthenticationClientImpl client, long opId,
+ String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
+ return;
+ }
+
+ mHandler.post(() -> {
+ mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
+
+ // Get performance stats object for this user.
+ HashMap<Integer, PerformanceStats> pmap
+ = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+ PerformanceStats stats = pmap.get(mCurrentUserId);
+ if (stats == null) {
+ stats = new PerformanceStats();
+ pmap.put(mCurrentUserId, stats);
+ }
+ mPerformanceStats = stats;
+
+ startAuthentication(client, opPackageName);
+ });
+ }
+
+ protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+ return;
+ }
+
+ mHandler.post(() -> {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof AuthenticationClient) {
+ if (client.getToken() == token) {
+ if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+ client.stop(client.getToken() == token);
+ } else {
+ if (DEBUG) Slog.v(getTag(), "can't stop client "
+ + client.getOwnerString() + " since tokens don't match");
+ }
+ } else if (client != null) {
+ if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+ + client.getOwnerString());
+ }
+ });
+ }
+
+ protected void setActiveUserInternal(int userId) {
+ mHandler.post(() -> {
+ updateActiveGroup(userId, null /* clientPackage */);
+ });
+ }
+
+ protected void removeInternal(RemovalClientImpl client) {
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ protected void enumerateInternal(EnumerateClientImpl client) {
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ // Should be done on a handler thread - not on the Binder's thread.
+ private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
+ updateActiveGroup(client.getGroupId(), opPackageName);
+
+ if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
+
+ int lockoutMode = getLockoutMode();
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "In lockout mode(" + lockoutMode +
+ ") ; disallowing authentication");
+ int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (!client.onError(errorCode, 0 /* vendorCode */)) {
+ Slog.w(getTag(), "Cannot send permanent lockout message to client");
+ }
+ return;
+ }
+ startClient(client, true /* initiatedByClient */);
+ }
+
+ /**
+ * Helper methods.
+ */
+
+ /**
+ * @param opPackageName name of package for caller
+ * @param requireForeground only allow this call while app is in the foreground
+ * @return true if caller can use the biometric API
+ */
+ protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
+ int pid, int userId) {
+ checkUseBiometricPermission();
+
+ if (isKeyguard(opPackageName)) {
+ return true; // Keyguard is always allowed
+ }
+ if (!isCurrentUserOrProfile(userId)) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
+ return false;
+ }
+ if (mAppOps.noteOp(getAppOp(), uid, opPackageName) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
+ return false;
+ }
+ if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
+ opPackageName))) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param opPackageName package of the caller
+ * @return true if this is the same client currently using the biometric
+ */
+ private boolean isCurrentClient(String opPackageName) {
+ return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+ }
+
+ /**
+ * @return true if this is keyguard package
+ */
+ private boolean isKeyguard(String clientPackage) {
+ return mKeyguardPackage.equals(clientPackage);
+ }
+
+ private int getLockoutMode() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+ if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
+ return AuthenticationClient.LOCKOUT_PERMANENT;
+ } else if (failedAttempts > 0 &&
+ mTimedLockoutCleared.get(currentUser, false) == false
+ && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
+ return AuthenticationClient.LOCKOUT_TIMED;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
+ private boolean isForegroundActivity(int uid, int pid) {
+ try {
+ List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == pid && proc.uid == uid
+ && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
+
+ /**
+ * Calls the HAL to switch states to the new task. If there's already a current task,
+ * it calls cancel() and sets mPendingClient to begin when the current task finishes
+ * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
+ *
+ * @param newClient the new client that wants to connect
+ * @param initiatedByClient true for authenticate, remove and enroll
+ */
+ private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+ ClientMonitor currentClient = mCurrentClient;
+ if (currentClient != null) {
+ if (DEBUG) Slog.v(getTag(), "request stop current client " +
+ currentClient.getOwnerString());
+
+ // This check only matters for FingerprintService, since enumerate may call back
+ // multiple times.
+ if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
+ currentClient instanceof FingerprintService.RemovalClientImpl) {
+ // This condition means we're currently running internal diagnostics to
+ // remove extra fingerprints in the hardware and/or the software
+ // TODO: design an escape hatch in case client never finishes
+ if (newClient != null) {
+ Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ }
+ } else {
+ currentClient.stop(initiatedByClient);
+ }
+ mPendingClient = newClient;
+ mHandler.removeCallbacks(mResetClientState);
+ mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
+ } else if (newClient != null) {
+ mCurrentClient = newClient;
+ if (DEBUG) Slog.v(getTag(), "starting client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ notifyClientActiveCallbacks(true);
+
+ newClient.start();
+ }
+ }
+
+ protected void removeClient(ClientMonitor client) {
+ if (client != null) {
+ client.destroy();
+ if (client != mCurrentClient && mCurrentClient != null) {
+ Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
+ + mCurrentClient.getOwnerString());
+ }
+ }
+ if (mCurrentClient != null) {
+ if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
+ mCurrentClient = null;
+ }
+ if (mPendingClient == null) {
+ notifyClientActiveCallbacks(false);
+ }
+ }
+
+ /**
+ * @param clientPackage the package of the caller
+ * @return the profile id
+ */
+ protected int getUserOrWorkProfileId(String clientPackage, int userId) {
+ if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+ return userId;
+ }
+ return getEffectiveUserId(userId);
+ }
+
+ protected boolean isRestricted() {
+ // Only give privileged apps (like Settings) access to biometric info
+ final boolean restricted = !hasPermission(getManageBiometricPermission());
+ return restricted;
+ }
+
+ protected boolean hasPermission(String permission) {
+ return getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ protected void checkPermission(String permission) {
+ getContext().enforceCallingOrSelfPermission(permission,
+ "Must have " + permission + " permission.");
+ }
+
+ protected boolean isCurrentUserOrProfile(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um == null) {
+ Slog.e(getTag(), "Unable to acquire UserManager");
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Allow current user or profiles of the current user...
+ for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+ if (profileId == userId) {
+ return true;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return false;
+ }
+
+ private void scheduleLockoutResetForUser(int userId) {
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ getLockoutResetIntentForUser(userId));
+ }
+
+ private PendingIntent getLockoutResetIntentForUser(int userId) {
+ return PendingIntent.getBroadcast(mContext, userId,
+ new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private void userActivity() {
+ long now = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ }
+
+ /**
+ * @param userId
+ * @return true if this is a work profile
+ */
+ private boolean isWorkProfile(int userId) {
+ UserInfo userInfo = null;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ userInfo = mUserManager.getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return userInfo != null && userInfo.isManagedProfile();
+ }
+
+
+ private int getEffectiveUserId(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um != null) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ userId = um.getCredentialOwnerProfile(userId);
+ Binder.restoreCallingIdentity(callingIdentity);
+ } else {
+ Slog.e(getTag(), "Unable to acquire UserManager");
+ }
+ return userId;
+ }
+
+ // Attempt counter should only be cleared when Keyguard goes away or when
+ // a biometric is successfully authenticated.
+ private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+ if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+ }
+ if (clearAttemptCounter) {
+ mFailedAttempts.put(userId, 0);
+ }
+ mTimedLockoutCleared.put(userId, true);
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+ // the alarm might still be pending; remove it.
+ cancelLockoutResetForUser(userId);
+ notifyLockoutResetMonitors();
+ }
+
+ private void cancelLockoutResetForUser(int userId) {
+ mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+ }
+
+ private void listenForUserSwitches() {
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) throws RemoteException {
+ mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+ .sendToTarget();
+ }
+ }, getTag());
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "Failed to listen for user switching event" ,e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
similarity index 66%
rename from services/core/java/com/android/server/fingerprint/ClientMonitor.java
rename to services/core/java/com/android/server/biometrics/common/ClientMonitor.java
index b935ba2..9147a40 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,16 +11,13 @@
* 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.
+ * limitations under the License
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.common;
-import android.Manifest;
import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
import android.os.IBinder;
import android.os.RemoteException;
@@ -28,23 +25,24 @@
import android.os.Vibrator;
import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
+
import java.util.NoSuchElementException;
/**
- * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
+ * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
* the current client. Subclasses are responsible for coordinating the interaction with
- * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
*/
public abstract class ClientMonitor implements IBinder.DeathRecipient {
- protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
- protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
- protected static final boolean DEBUG = FingerprintService.DEBUG;
- private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
+ protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
+ protected static final boolean DEBUG = BiometricService.DEBUG;
private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
+
private final Context mContext;
private final long mHalDeviceId;
private final int mTargetUserId;
@@ -54,51 +52,65 @@
private final String mOwner;
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
+ private final BiometricService.DaemonWrapper mDaemon;
+
private IBinder mToken;
- private IFingerprintServiceReceiver mReceiver;
+ private BiometricService.ServiceListener mListener;
+
+ protected final MetricsLogger mMetricsLogger;
+ protected final Metrics mMetrics;
+
protected boolean mAlreadyCancelled;
/**
- * @param context context of FingerprintService
- * @param halDeviceId the HAL device ID of the associated fingerprint hardware
+ * @param context context of BiometricService
+ * @param daemon interface to call back to a specific biometric's daemon
+ * @param halDeviceId the HAL device ID of the associated biometric hardware
* @param token a unique token for the client
- * @param receiver recipient of related events (e.g. authentication)
+ * @param listener recipient of related events (e.g. authentication)
* @param userId target user id for operation
* @param groupId groupId for the fingerprint set
- * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
+ * @param restricted whether or not client has the MANAGE_* permission
* permission
* @param owner name of the client that owns this
*/
- public ClientMonitor(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
- String owner) {
+ public ClientMonitor(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
+ int groupId, boolean restricted, String owner) {
mContext = context;
+ mMetrics = metrics;
+ mDaemon = daemon;
mHalDeviceId = halDeviceId;
mToken = token;
- mReceiver = receiver;
+ mListener = listener;
mTargetUserId = userId;
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ mMetricsLogger = new MetricsLogger();
try {
if (token != null) {
token.linkToDeath(this, 0);
}
} catch (RemoteException e) {
- Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ Slog.w(getLogTag(), "caught remote exception in linkToDeath: ", e);
}
}
+ protected String getLogTag() {
+ return mMetrics.logTag();
+ }
+
/**
- * Contacts fingerprint HAL to start the client.
+ * Contacts the biometric's HAL to start the client.
* @return 0 on success, errno from driver on failure
*/
public abstract int start();
/**
- * Contacts fingerprint HAL to stop the client.
+ * Contacts the biometric's HAL to stop the client.
* @param initiatedByClient whether the operation is at the request of a client
*/
public abstract int stop(boolean initiatedByClient);
@@ -108,56 +120,47 @@
*/
public abstract void notifyUserActivity();
- /**
- * Gets the fingerprint daemon from the cached state in the container class.
- */
- public abstract IBiometricsFingerprint getFingerprintDaemon();
-
// Event callbacks from driver. Inappropriate calls is flagged/logged by the
// respective client (e.g. enrolling shouldn't get authenticate events).
// All of these return 'true' if the operation is completed and it's ok to move
- // to the next client (e.g. authentication accepts or rejects a fingerprint).
+ // to the next client (e.g. authentication accepts or rejects a biometric).
public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
public abstract boolean onAuthenticated(int fingerId, int groupId);
public abstract boolean onRemoved(int fingerId, int groupId, int remaining);
public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);
/**
- * Called when we get notification from fingerprint HAL that an image has been acquired.
+ * Called when we get notification from the biometric's HAL that an image has been acquired.
* Common to authenticate and enroll.
* @param acquiredInfo info about the current image acquisition
* @return true if client should be removed
*/
public boolean onAcquired(int acquiredInfo, int vendorCode) {
- if (mReceiver == null)
- return true; // client not connected
try {
- mReceiver.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
+ mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
return false; // acquisition continues...
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke sendAcquired:", e);
- return true; // client failed
+ Slog.w(getLogTag(), "Failed to invoke sendAcquired", e);
+ return true;
} finally {
// Good scans will keep the device awake
- if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
notifyUserActivity();
}
}
}
/**
- * Called when we get notification from fingerprint HAL that an error has occurred with the
+ * Called when we get notification from the biometric's HAL that an error has occurred with the
* current operation. Common to authenticate, enroll, enumerate and remove.
* @param error
* @return true if client should be removed
*/
public boolean onError(int error, int vendorCode) {
- if (mReceiver != null) {
- try {
- mReceiver.onError(getHalDeviceId(), error, vendorCode);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke sendError:", e);
- }
+ try {
+ mListener.onError(getHalDeviceId(), error, vendorCode);
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to invoke sendError", e);
}
return true; // errors always remove current client
}
@@ -168,26 +171,26 @@
mToken.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
// TODO: remove when duplicate call bug is found
- Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+ Slog.e(getLogTag(), "destroy(): " + this + ":", new Exception("here"));
}
mToken = null;
}
- mReceiver = null;
+ mListener = null;
}
@Override
public void binderDied() {
mToken = null;
- mReceiver = null;
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mListener = null;
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
}
@Override
protected void finalize() throws Throwable {
try {
if (mToken != null) {
- if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken);
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
}
} finally {
super.finalize();
@@ -206,8 +209,12 @@
return mOwner;
}
- public final IFingerprintServiceReceiver getReceiver() {
- return mReceiver;
+ public final BiometricService.ServiceListener getListener() {
+ return mListener;
+ }
+
+ public final BiometricService.DaemonWrapper getDaemonWrapper() {
+ return mDaemon;
}
public final boolean getIsRestricted() {
diff --git a/services/core/java/com/android/server/biometrics/common/EnrollClient.java b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
new file mode 100644
index 0000000..6fb8ffe
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
@@ -0,0 +1,132 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.fingerprint.FingerprintUtils;
+
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+ private static final long MS_PER_SEC = 1000;
+ private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+ private byte[] mCryptoToken;
+
+ public EnrollClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
+ int groupId, byte[] cryptoToken, boolean restricted, String owner) {
+ super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ owner);
+ mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+ }
+
+ @Override
+ public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
+ if (groupId != getGroupId()) {
+ Slog.w(getLogTag(), "groupId != getGroupId(), groupId: " + groupId +
+ " getGroupId():" + getGroupId());
+ }
+ if (remaining == 0) {
+ // TODO: handle other biometrics
+ FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
+ getTargetUserId());
+ }
+ return sendEnrollResult(fingerId, groupId, remaining);
+ }
+
+ /*
+ * @return true if we're done.
+ */
+ private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
+ vibrateSuccess();
+ mMetricsLogger.action(mMetrics.actionBiometricEnroll());
+ try {
+ getListener().onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
+ return remaining == 0;
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to notify EnrollResult:", e);
+ return true;
+ }
+ }
+
+ @Override
+ public int start() {
+ final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+ try {
+ final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout);
+ if (result != 0) {
+ Slog.w(getLogTag(), "startEnroll failed, result=" + result);
+ mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "startEnroll failed", e);
+ }
+ return 0; // success
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(getLogTag(), "stopEnroll: already cancelled!");
+ return 0;
+ }
+
+ try {
+ final int result = getDaemonWrapper().cancel();
+ if (result != 0) {
+ Slog.w(getLogTag(), "startEnrollCancel failed, result = " + result);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "stopEnrollment failed", e);
+ }
+ if (initiatedByClient) {
+ onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */);
+ }
+ mAlreadyCancelled = true;
+ return 0;
+ }
+
+ @Override
+ public boolean onRemoved(int fingerId, int groupId, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+ @Override
+ public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+ @Override
+ public boolean onAuthenticated(int fingerId, int groupId) {
+ if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/common/EnumerateClient.java b/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
new file mode 100644
index 0000000..fa3a077
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
@@ -0,0 +1,108 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the enumeration state for a given client.
+ */
+public abstract class EnumerateClient extends ClientMonitor {
+ public EnumerateClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int groupId,
+ int userId, boolean restricted, String owner) {
+ super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ owner);
+ }
+
+ @Override
+ public int start() {
+ // The biometric template ids will be removed when we get confirmation from the HAL
+ try {
+ final int result = getDaemonWrapper().enumerate();
+ if (result != 0) {
+ Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId()
+ + " failed, result=" + result);
+ mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result);
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "startEnumeration failed", e);
+ }
+ return 0;
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(getLogTag(), "stopEnumerate: already cancelled!");
+ return 0;
+ }
+
+ try {
+ final int result = getDaemonWrapper().cancel();
+ if (result != 0) {
+ Slog.w(getLogTag(), "stop enumeration failed, result=" + result);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "stopEnumeration failed", e);
+ return ERROR_ESRCH;
+ }
+
+ // We don't actually stop enumerate, but inform the client that the cancel operation
+ // succeeded so we can start the next operation.
+ if (initiatedByClient) {
+ onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */);
+ }
+ mAlreadyCancelled = true;
+ return 0; // success
+ }
+
+ @Override
+ public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
+ try {
+ getListener().onEnumerated(getHalDeviceId(), fingerId, groupId, remaining);
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to notify enumerated:", e);
+ }
+ return remaining == 0;
+ }
+
+ @Override
+ public boolean onAuthenticated(int fingerId, int groupId) {
+ if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enumerate!");
+ return true; // Invalid for Enumerate.
+ }
+
+ @Override
+ public boolean onEnrollResult(int fingerId, int groupId, int rem) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for enumerate!");
+ return true; // Invalid for Enumerate.
+ }
+
+ @Override
+ public boolean onRemoved(int fingerId, int groupId, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enumerate!");
+ return true; // Invalid for Enumerate.
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/common/Metrics.java b/services/core/java/com/android/server/biometrics/common/Metrics.java
new file mode 100644
index 0000000..eb1a1f8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/Metrics.java
@@ -0,0 +1,34 @@
+/*
+ * 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.biometrics.common;
+
+public interface Metrics {
+ /** The log tag */
+ String logTag();
+
+ /** Strings for MetricsLogger.count() and MetricsLogger.histogram() */
+ String tagHalDied();
+ String tagAuthToken();
+ String tagAuthStartError();
+ String tagEnrollStartError();
+ String tagEnumerateStartError();
+ String tagRemoveStartError();
+
+ /** Integers for MetricsLogger.action() */
+ int actionBiometricAuth();
+ int actionBiometricEnroll();
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/common/RemovalClient.java b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
new file mode 100644
index 0000000..1555a6c
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
@@ -0,0 +1,120 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.fingerprint.FingerprintUtils;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+ private int mFingerId;
+
+ public RemovalClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener,
+ int fingerId, int groupId, int userId, boolean restricted, String owner) {
+ super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ owner);
+ mFingerId = fingerId;
+ }
+
+ @Override
+ public int start() {
+ // The biometric template ids will be removed when we get confirmation from the HAL
+ try {
+ final int result = getDaemonWrapper().remove(getGroupId(), mFingerId);
+ if (result != 0) {
+ Slog.w(getLogTag(), "startRemove with id = " + mFingerId + " failed, result=" +
+ result);
+ mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result);
+ onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "startRemove failed", e);
+ }
+ return 0;
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(getLogTag(), "stopRemove: already cancelled!");
+ return 0;
+ }
+
+ try {
+ final int result = getDaemonWrapper().cancel();
+ if (result != 0) {
+ Slog.w(getLogTag(), "stopRemoval failed, result=" + result);
+ return result;
+ }
+ if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer removing");
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "stopRemoval failed", e);
+ return ERROR_ESRCH;
+ }
+ mAlreadyCancelled = true;
+ return 0; // success
+ }
+
+ /*
+ * @return true if we're done.
+ */
+ private boolean sendRemoved(int fingerId, int groupId, int remaining) {
+ try {
+ getListener().onRemoved(getHalDeviceId(), fingerId, groupId, remaining);
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to notify Removed:", e);
+ }
+ return remaining == 0;
+ }
+
+ @Override
+ public boolean onRemoved(int fingerId, int groupId, int remaining) {
+ if (fingerId != 0) {
+ // TODO: biometric
+ FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
+ getTargetUserId());
+ }
+ return sendRemoved(fingerId, getGroupId(), remaining);
+ }
+
+ @Override
+ public boolean onEnrollResult(int fingerId, int groupId, int rem) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for remove!");
+ return true; // Invalid for Remove
+ }
+
+ @Override
+ public boolean onAuthenticated(int fingerId, int groupId) {
+ if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for remove!");
+ return true; // Invalid for Remove.
+ }
+
+ @Override
+ public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for remove!");
+ return true; // Invalid for Remove.
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/face/AuthenticationClient.java
new file mode 100644
index 0000000..f9e8793
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/AuthenticationClient.java
@@ -0,0 +1,169 @@
+/**
+ * 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.biometrics.face;
+
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import android.content.Context;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the authentication state for a given client.
+ */
+public abstract class AuthenticationClient extends ClientMonitor {
+ private long mOpId;
+
+ public abstract int handleFailedAttempt();
+ public abstract void resetFailedAttempts();
+
+ public static final int LOCKOUT_NONE = 0;
+ public static final int LOCKOUT_TIMED = 1;
+ public static final int LOCKOUT_PERMANENT = 2;
+
+ public AuthenticationClient(Context context, long halDeviceId, IBinder token,
+ IFaceServiceReceiver receiver, int targetUserId, long opId,
+ boolean restricted, String owner) {
+ super(context, halDeviceId, token, receiver, targetUserId, restricted, owner);
+ mOpId = opId;
+ }
+
+ @Override
+ public boolean onAuthenticated(int faceId) {
+ boolean result = false;
+ boolean authenticated = faceId != 0;
+
+ IFaceServiceReceiver receiver = getReceiver();
+ if (receiver != null) {
+ try {
+ MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
+ authenticated); // TODO: Define ACTION_FACE_AUTH constant and use here
+ if (!authenticated) {
+ receiver.onAuthenticationFailed(getHalDeviceId());
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString());
+ }
+ Face face = !getIsRestricted()
+ ? new Face("" /* TODO */, faceId, getHalDeviceId())
+ : null;
+ receiver.onAuthenticationSucceeded(getHalDeviceId(), face);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify Authenticated:", e);
+ result = true; // client failed
+ }
+ } else {
+ result = true; // client not listening
+ }
+ if (!authenticated) {
+ // allow system-defined limit of number of attempts before giving up
+ int lockoutMode = handleFailedAttempt();
+ if (lockoutMode != LOCKOUT_NONE) {
+ try {
+ Slog.w(TAG, "Forcing lockout (face authentication driver code should do " +
+ "this!), mode(" + lockoutMode + ")");
+ stop(false);
+ int errorCode = lockoutMode == LOCKOUT_TIMED ?
+ FaceManager.FACE_ERROR_LOCKOUT :
+ FaceManager.FACE_ERROR_LOCKOUT_PERMANENT;
+ receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify lockout:", e);
+ }
+ }
+ result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
+ } else {
+ if (receiver != null) {
+ vibrateSuccess();
+ }
+ result |= true; // we have a valid face, done
+ resetFailedAttempts();
+ }
+ return result;
+ }
+
+ /**
+ * Start authentication
+ */
+ @Override
+ public int start() {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "start authentication: no face HAL!");
+ return ERROR_ESRCH;
+ }
+ try {
+ final int result = daemon.authenticate(mOpId);
+ if (result != 0) {
+ Slog.w(TAG, "startAuthentication failed, result=" + result);
+ MetricsLogger.histogram(getContext(), "faced_auth_start_error", result);
+ onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ return result;
+ }
+ if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startAuthentication failed", e);
+ return ERROR_ESRCH;
+ }
+ return 0; // success
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(TAG, "stopAuthentication: already cancelled!");
+ return 0;
+ }
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "stopAuthentication: no face HAL!");
+ return ERROR_ESRCH;
+ }
+ try {
+ final int result = daemon.cancel();
+ if (result != 0) {
+ Slog.w(TAG, "stopAuthentication failed, result=" + result);
+ return result;
+ }
+ if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "stopAuthentication failed", e);
+ return ERROR_ESRCH;
+ }
+ mAlreadyCancelled = true;
+ return 0; // success
+ }
+
+ @Override
+ public boolean onEnrollResult(int faceId, int remaining) {
+ if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
+ return true; // Invalid for Authenticate
+ }
+
+ @Override
+ public boolean onRemoved(int faceId, int remaining) {
+ if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
+ return true; // Invalid for Authenticate
+ }
+}
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/biometrics/face/ClientMonitor.java
similarity index 63%
copy from services/core/java/com/android/server/fingerprint/ClientMonitor.java
copy to services/core/java/com/android/server/biometrics/face/ClientMonitor.java
index b935ba2..4b13db9 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/face/ClientMonitor.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.face;
import android.Manifest;
import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.media.AudioAttributes;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.VibrationEffect;
@@ -31,57 +30,47 @@
import java.util.NoSuchElementException;
/**
- * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
- * the current client. Subclasses are responsible for coordinating the interaction with
- * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ * Abstract base class for keeping track and dispatching events from face HAL to the
+ * current client. Subclasses are responsible for coordinating the interaction with
+ * face HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
*/
public abstract class ClientMonitor implements IBinder.DeathRecipient {
- protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
- protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
- protected static final boolean DEBUG = FingerprintService.DEBUG;
+ protected static final String TAG = FaceService.TAG; // TODO: get specific name
+ protected static final int ERROR_ESRCH = 3; // Likely face HAL is dead. See errno.h.
+ protected static final boolean DEBUG = FaceService.DEBUG;
private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
- private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
- new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
private final Context mContext;
private final long mHalDeviceId;
private final int mTargetUserId;
- private final int mGroupId;
- // True if client does not have MANAGE_FINGERPRINT permission
+ // True if client does not have MANAGE_FACE_AUTHENTICATION permission
private final boolean mIsRestricted;
private final String mOwner;
private final VibrationEffect mSuccessVibrationEffect;
- private final VibrationEffect mErrorVibrationEffect;
private IBinder mToken;
- private IFingerprintServiceReceiver mReceiver;
+ private IFaceServiceReceiver mReceiver;
protected boolean mAlreadyCancelled;
/**
- * @param context context of FingerprintService
- * @param halDeviceId the HAL device ID of the associated fingerprint hardware
+ * @param context context of FaceService
+ * @param halDeviceId the HAL device ID of the associated face authentication hardware
* @param token a unique token for the client
* @param receiver recipient of related events (e.g. authentication)
* @param userId target user id for operation
- * @param groupId groupId for the fingerprint set
- * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
+ * @param restricted whether or not client has the {@link Manifest#MANAGE_FACE}
* permission
* @param owner name of the client that owns this
*/
public ClientMonitor(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
+ IFaceServiceReceiver receiver, int userId, boolean restricted,
String owner) {
mContext = context;
mHalDeviceId = halDeviceId;
mToken = token;
mReceiver = receiver;
mTargetUserId = userId;
- mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
- mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ mSuccessVibrationEffect = getSuccessVibrationEffect(context);
try {
if (token != null) {
token.linkToDeath(this, 0);
@@ -92,13 +81,13 @@
}
/**
- * Contacts fingerprint HAL to start the client.
+ * Contacts face HAL to start the client.
* @return 0 on success, errno from driver on failure
*/
public abstract int start();
/**
- * Contacts fingerprint HAL to stop the client.
+ * Contacts face HAL to stop the client.
* @param initiatedByClient whether the operation is at the request of a client
*/
public abstract int stop(boolean initiatedByClient);
@@ -109,21 +98,20 @@
public abstract void notifyUserActivity();
/**
- * Gets the fingerprint daemon from the cached state in the container class.
+ * Gets the face daemon from the cached state in the container class.
*/
- public abstract IBiometricsFingerprint getFingerprintDaemon();
+ public abstract IBiometricsFace getFaceDaemon();
// Event callbacks from driver. Inappropriate calls is flagged/logged by the
// respective client (e.g. enrolling shouldn't get authenticate events).
// All of these return 'true' if the operation is completed and it's ok to move
- // to the next client (e.g. authentication accepts or rejects a fingerprint).
- public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
- public abstract boolean onAuthenticated(int fingerId, int groupId);
- public abstract boolean onRemoved(int fingerId, int groupId, int remaining);
- public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);
+ // to the next client (e.g. authentication accepts or rejects a face).
+ public abstract boolean onEnrollResult(int faceId, int remaining);
+ public abstract boolean onAuthenticated(int faceId);
+ public abstract boolean onRemoved(int faceId, int remaining);
/**
- * Called when we get notification from fingerprint HAL that an image has been acquired.
+ * Called when we get notification from face HAL that an image has been acquired.
* Common to authenticate and enroll.
* @param acquiredInfo info about the current image acquisition
* @return true if client should be removed
@@ -139,14 +127,14 @@
return true; // client failed
} finally {
// Good scans will keep the device awake
- if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo == FaceManager.FACE_ACQUIRED_GOOD) {
notifyUserActivity();
}
}
}
/**
- * Called when we get notification from fingerprint HAL that an error has occurred with the
+ * Called when we get notification from face HAL that an error has occurred with the
* current operation. Common to authenticate, enroll, enumerate and remove.
* @param error
* @return true if client should be removed
@@ -179,7 +167,7 @@
public void binderDied() {
mToken = null;
mReceiver = null;
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
}
@Override
@@ -187,7 +175,7 @@
try {
if (mToken != null) {
if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
}
} finally {
super.finalize();
@@ -206,7 +194,7 @@
return mOwner;
}
- public final IFingerprintServiceReceiver getReceiver() {
+ public final IFaceServiceReceiver getReceiver() {
return mReceiver;
}
@@ -218,10 +206,6 @@
return mTargetUserId;
}
- public final int getGroupId() {
- return mGroupId;
- }
-
public final IBinder getToken() {
return mToken;
}
@@ -229,14 +213,31 @@
public final void vibrateSuccess() {
Vibrator vibrator = mContext.getSystemService(Vibrator.class);
if (vibrator != null) {
- vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
+ vibrator.vibrate(mSuccessVibrationEffect);
}
}
- public final void vibrateError() {
- Vibrator vibrator = mContext.getSystemService(Vibrator.class);
- if (vibrator != null) {
- vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
+// public final void vibrateError() {
+// }
+
+ private static VibrationEffect getSuccessVibrationEffect(Context ctx) {
+ int[] arr = ctx.getResources().getIntArray(
+ com.android.internal.R.array.config_longPressVibePattern);
+ final long[] vibePattern;
+ if (arr == null || arr.length == 0) {
+ vibePattern = DEFAULT_SUCCESS_VIBRATION_PATTERN;
+ } else {
+ vibePattern = new long[arr.length];
+ for (int i = 0; i < arr.length; i++) {
+ vibePattern[i] = arr[i];
+ }
+ }
+ if (vibePattern.length == 1) {
+ return VibrationEffect.createOneShot(
+ vibePattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
+ } else {
+ return VibrationEffect.createWaveform(vibePattern, -1);
}
}
+
}
diff --git a/services/core/java/com/android/server/biometrics/face/EnrollClient.java b/services/core/java/com/android/server/biometrics/face/EnrollClient.java
new file mode 100644
index 0000000..91e489a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/EnrollClient.java
@@ -0,0 +1,142 @@
+/**
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+ private static final long MS_PER_SEC = 1000;
+ private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+ private byte[] mCryptoToken;
+
+ public EnrollClient(Context context, long halDeviceId, IBinder token,
+ IFaceServiceReceiver receiver, int userId, byte [] cryptoToken, boolean restricted,
+ String owner) {
+ super(context, halDeviceId, token, receiver, userId, restricted, owner);
+ mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+ }
+
+ @Override
+ public boolean onEnrollResult(int faceId, int remaining) {
+ if (remaining == 0) {
+ FaceUtils.getInstance().addFaceForUser(getContext(), faceId, getTargetUserId());
+ }
+ return sendEnrollResult(faceId, remaining);
+ }
+
+ /*
+ * @return true if we're done.
+ */
+ private boolean sendEnrollResult(int faceId, int remaining) {
+ IFaceServiceReceiver receiver = getReceiver();
+ if (receiver == null)
+ return true; // client not listening
+
+ vibrateSuccess();
+ MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
+ // TODO: create ACTION_FACE_ENROLL constant and use it here
+ try {
+ receiver.onEnrollResult(getHalDeviceId(), faceId, remaining);
+ return remaining == 0;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify EnrollResult:", e);
+ return true;
+ }
+ }
+
+ @Override
+ public int start() {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enroll: no face HAL!");
+ return ERROR_ESRCH;
+ }
+ final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+ try {
+ // ugh...
+ final ArrayList<Byte> token = new ArrayList<>();
+ for (int i = 0; i < mCryptoToken.length; i++) {
+ token.add(mCryptoToken[i]);
+ }
+ final int result = daemon.enroll(token, timeout);
+ if (result != 0) {
+ Slog.w(TAG, "startEnroll failed, result=" + result);
+ MetricsLogger.histogram(getContext(), "faced_enroll_start_error", result);
+ onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startEnroll failed", e);
+ }
+ return 0; // success
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(TAG, "stopEnroll: already cancelled!");
+ return 0;
+ }
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "stopEnrollment: no face HAL!");
+ return ERROR_ESRCH;
+ }
+ try {
+ final int result = daemon.cancel();
+ if (result != 0) {
+ Slog.w(TAG, "startEnrollCancel failed, result = " + result);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "stopEnrollment failed", e);
+ }
+ if (initiatedByClient) {
+ onError(FaceManager.FACE_ERROR_CANCELED, 0 /* vendorCode */);
+ }
+ mAlreadyCancelled = true;
+ return 0;
+ }
+
+ @Override
+ public boolean onRemoved(int faceId, int remaining) {
+ if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+ @Override
+ public boolean onAuthenticated(int faceId) {
+ if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
new file mode 100644
index 0000000..a9a5ad0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -0,0 +1,1254 @@
+/*
+ * 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.biometrics.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.RESET_FACE_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.app.SynchronousUserSwitchObserver;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
+import android.hardware.face.IFaceServiceLockoutResetCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.KeyStore;
+import android.service.face.FaceActionStatsProto;
+import android.service.face.FaceServiceDumpProto;
+import android.service.face.FaceUserStatsProto;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.SystemService;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A service to manage multiple clients that want to access the face HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * face -related events.
+ *
+ * @hide
+ */
+public class FaceService extends SystemService implements IHwBinder.DeathRecipient {
+ static final String TAG = "FaceService";
+ static final boolean DEBUG = true;
+ private static final String FACE_DATA_DIR = "facedata";
+ private static final int MSG_USER_SWITCHING = 10;
+ private static final String ACTION_LOCKOUT_RESET =
+ "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
+
+ private class PerformanceStats {
+ int accept; // number of accepted faces
+ int reject; // number of rejected faces
+ int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
+ // acquisition in some cases (too high, too low, poor gaze, etc.)
+ int lockout; // total number of lockouts
+ int permanentLockout; // total number of permanent lockouts
+ }
+
+ private final ArrayList<FaceServiceLockoutResetMonitor> mLockoutMonitors =
+ new ArrayList<>();
+ private final Map<Integer, Long> mAuthenticatorIds =
+ Collections.synchronizedMap(new HashMap<>());
+ private final AppOpsManager mAppOps;
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+
+ private static final long CANCEL_TIMEOUT_LIMIT_MS = 3000; // max wait for onCancel() from HAL,in ms
+ private final String mKeyguardPackage;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private final FaceUtils mFaceUtils = FaceUtils.getInstance();
+ private Context mContext;
+ private long mHalDeviceId;
+ private boolean mTimedLockoutCleared;
+ private int mFailedAttempts;
+ @GuardedBy("this")
+ private IBiometricsFace mDaemon;
+ private final PowerManager mPowerManager;
+ private final AlarmManager mAlarmManager;
+ private final UserManager mUserManager;
+ private ClientMonitor mCurrentClient;
+ private ClientMonitor mPendingClient;
+ private PerformanceStats mPerformanceStats;
+
+
+ private IBinder mToken = new Binder(); // used for internal FaceService enumeration
+
+ private class UserFace {
+ Face f;
+ int userId;
+ public UserFace(Face f, int userId) {
+ this.f = f;
+ this.userId = userId;
+ }
+ }
+
+ // Normal face authentications are tracked by mPerformanceMap.
+ private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
+
+ // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+ private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_USER_SWITCHING:
+ handleUserSwitching(msg.arg1);
+ break;
+
+ default:
+ Slog.w(TAG, "Unknown message:" + msg.what);
+ }
+ }
+ };
+
+ private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
+ resetFailedAttempts(false /* clearAttemptCounter */);
+ }
+ }
+ };
+
+ private final Runnable mResetFailedAttemptsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ resetFailedAttempts(true /* clearAttemptCounter */);
+ }
+ };
+
+ private final Runnable mResetClientState = new Runnable() {
+ @Override
+ public void run() {
+ // Warning: if we get here, the driver never confirmed our call to cancel the current
+ // operation (authenticate, enroll, remove, enumerate, etc), which is
+ // really bad. The result will be a 3-second delay in starting each new client.
+ // If you see this on a device, make certain the driver notifies with
+ // {@link FaceAuthenticationManager#FACE_ERROR_CANCEL} in response to cancel()
+ // once it has successfully switched to the IDLE state in the face HAL.
+ // Additionally,{@link FaceAuthenticationManager#FACE_ERROR_CANCEL} should only be sent
+ // in response to an actual cancel() call.
+ Slog.w(TAG, "Client "
+ + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+ + " failed to respond to cancel, starting client "
+ + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+
+ mCurrentClient = null;
+ startClient(mPendingClient, false);
+ }
+ };
+
+ public FaceService(Context context) {
+ super(context);
+ mContext = context;
+ mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
+ RESET_FACE_LOCKOUT, null /* handler */);
+ mUserManager = UserManager.get(mContext);
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.v(TAG, "face HAL died");
+ MetricsLogger.count(mContext, "faced_died", 1);
+ handleError(mHalDeviceId, FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+ 0 /*vendorCode */);
+ }
+
+ public synchronized IBiometricsFace getFaceDaemon() {
+ if (mDaemon == null) {
+ Slog.v(TAG, "mDaemon was null, reconnect to face");
+ try {
+ mDaemon = IBiometricsFace.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened. Logged below.
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get biometric interface", e);
+ }
+ if (mDaemon == null) {
+ Slog.w(TAG, "face HIDL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0);
+
+ try {
+ mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to open face HAL", e);
+ mDaemon = null; // try again later!
+ }
+
+ if (DEBUG) Slog.v(TAG, "face HAL id: " + mHalDeviceId);
+ if (mHalDeviceId != 0) {
+ loadAuthenticatorIds();
+ updateActiveGroup(ActivityManager.getCurrentUser(), null);
+ } else {
+ Slog.w(TAG, "Failed to open Face HAL!");
+ MetricsLogger.count(mContext, "faced_openhal_error", 1);
+ mDaemon = null;
+ }
+ }
+ return mDaemon;
+ }
+
+ /** Populates existing authenticator ids. To be used only during the start of the service. */
+ private void loadAuthenticatorIds() {
+ // This operation can be expensive, so keep track of the elapsed time. Might need to move to
+ // background if it takes too long.
+ long t = System.currentTimeMillis();
+ mAuthenticatorIds.clear();
+ for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+ int userId = getUserOrWorkProfileId(null, user.id);
+ if (!mAuthenticatorIds.containsKey(userId)) {
+ updateActiveGroup(userId, null);
+ }
+ }
+
+ t = System.currentTimeMillis() - t;
+ if (t > 1000) {
+ Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
+ }
+ }
+
+ protected void handleError(long deviceId, int error, int vendorCode) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onError(error, vendorCode)) {
+ removeClient(client);
+ }
+
+ if (DEBUG) Slog.v(TAG, "handleError(client="
+ + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+ // This is the magic code that starts the next client when the old client finishes.
+ if (error == FaceManager.FACE_ERROR_CANCELED) {
+ mHandler.removeCallbacks(mResetClientState);
+ if (mPendingClient != null) {
+ if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
+ startClient(mPendingClient, false);
+ mPendingClient = null;
+ }
+ } else if (error == FaceManager.FACE_ERROR_HW_UNAVAILABLE) {
+ // If we get HW_UNAVAILABLE, try to connect again later...
+ Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+ synchronized (this) {
+ mDaemon = null;
+ mHalDeviceId = 0;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ }
+ }
+
+ protected void handleRemoved(long deviceId, int faceId, int userId, int remaining) {
+ if (DEBUG) Slog.w(TAG, "Removed: fid=" + faceId
+ + ", uid=" + userId
+ + ", dev=" + deviceId
+ + ", rem=" + remaining);
+
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onRemoved(faceId, remaining)) {
+ removeClient(client);
+ }
+ }
+
+ protected void handleAuthenticated(long deviceId, int faceId, int userId,
+ ArrayList<Byte> token) {
+ ClientMonitor client = mCurrentClient;
+ if (faceId != 0) {
+ // Ugh...
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
+ }
+ // Send to Keystore
+ KeyStore.getInstance().addAuthToken(byteToken);
+ }
+ if (client != null && client.onAuthenticated(faceId)) {
+ removeClient(client);
+ }
+ if (faceId != 0) {
+ mPerformanceStats.accept++;
+ } else {
+ mPerformanceStats.reject++;
+ }
+ }
+
+ protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
+ removeClient(client);
+ }
+ if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+ && client instanceof AuthenticationClient) {
+ // ignore enrollment acquisitions or acquisitions when we're locked out
+ mPerformanceStats.acquire++;
+ }
+ }
+
+ protected void handleEnrollResult(long deviceId, int faceId, int userId, int remaining) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onEnrollResult(faceId, remaining)) {
+ removeClient(client);
+ }
+ }
+
+ private void userActivity() {
+ long now = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ }
+
+ void handleUserSwitching(int userId) {
+ updateActiveGroup(userId, null);
+ }
+
+ private void removeClient(ClientMonitor client) {
+ if (client != null) {
+ client.destroy();
+ if (client != mCurrentClient && mCurrentClient != null) {
+ Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
+ + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
+ }
+ }
+ if (mCurrentClient != null) {
+ if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
+ mCurrentClient = null;
+ }
+ }
+
+ private int getLockoutMode() {
+ if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+ return AuthenticationClient.LOCKOUT_PERMANENT;
+ } else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
+ (mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+ return AuthenticationClient.LOCKOUT_TIMED;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
+ private void scheduleLockoutReset() {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent());
+ }
+
+ private void cancelLockoutReset() {
+ mAlarmManager.cancel(getLockoutResetIntent());
+ }
+
+ private PendingIntent getLockoutResetIntent() {
+ return PendingIntent.getBroadcast(mContext, 0,
+ new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ public long startPreEnroll(IBinder token) {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPreEnroll: no face HAL!");
+ return 0;
+ }
+ try {
+ return daemon.preEnroll().value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPreEnroll failed", e);
+ }
+ return 0;
+ }
+
+ public int startPostEnroll(IBinder token) {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPostEnroll: no face HAL!");
+ return 0;
+ }
+ try {
+ return daemon.postEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPostEnroll failed", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Calls face HAL to switch states to the new task. If there's already a current task,
+ * it calls cancel() and sets mPendingClient to begin when the current task finishes
+ * ({@link FaceManager#FACE_ERROR_CANCELED}).
+ * @param newClient the new client that wants to connect
+ * @param initiatedByClient true for authenticate, remove and enroll
+ */
+ private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+ ClientMonitor currentClient = mCurrentClient;
+ if (currentClient != null) {
+ if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
+ if (currentClient instanceof InternalRemovalClient) {
+ // This condition means we're currently running internal diagnostics to
+ // remove a face in the hardware and/or the software
+ // TODO: design an escape hatch in case client never finishes
+ }
+ else {
+ currentClient.stop(initiatedByClient);
+ }
+ mPendingClient = newClient;
+ mHandler.removeCallbacks(mResetClientState);
+ mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT_MS);
+ } else if (newClient != null) {
+ mCurrentClient = newClient;
+ if (DEBUG) Slog.v(TAG, "starting client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient + ")");
+ newClient.start();
+ }
+ }
+
+ void startRemove(IBinder token, int userId, IFaceServiceReceiver receiver, boolean restricted,
+ boolean internal) {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startRemove: no face HAL!");
+ return;
+ }
+
+ if (internal) {
+ Context context = getContext();
+ InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
+ token, receiver, userId, restricted, context.getOpPackageName()) {
+ @Override
+ public void notifyUserActivity() {
+
+ }
+ @Override
+ public IBiometricsFace getFaceDaemon() {
+ return FaceService.this.getFaceDaemon();
+ }
+ };
+ startClient(client, true);
+ }
+ else {
+ RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
+ receiver, userId, restricted, token.toString()) {
+ @Override
+ public void notifyUserActivity() {
+ FaceService.this.userActivity();
+ }
+
+ @Override
+ public IBiometricsFace getFaceDaemon() {
+ return FaceService.this.getFaceDaemon();
+ }
+ };
+ startClient(client, true);
+ }
+ }
+
+ /*
+ * @hide
+ */
+ public Face getEnrolledFace(int userId) {
+ return mFaceUtils.getFaceForUser(mContext, userId);
+ }
+
+ public boolean hasEnrolledFace(int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ checkPermission(INTERACT_ACROSS_USERS);
+ }
+ return mFaceUtils.getFaceForUser(mContext, userId) != null;
+ }
+
+ boolean hasPermission(String permission) {
+ return getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ void checkPermission(String permission) {
+ getContext().enforceCallingOrSelfPermission(permission,
+ "Must have " + permission + " permission.");
+ }
+
+ int getEffectiveUserId(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um != null) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ userId = um.getCredentialOwnerProfile(userId);
+ Binder.restoreCallingIdentity(callingIdentity);
+ } else {
+ Slog.e(TAG, "Unable to acquire UserManager");
+ }
+ return userId;
+ }
+
+ boolean isCurrentUserOrProfile(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um == null) {
+ Slog.e(TAG, "Unable to acquire UserManager");
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Allow current user or profiles of the current user...
+ for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+ if (profileId == userId) {
+ return true;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return false;
+ }
+
+ private boolean isForegroundActivity(int uid, int pid) {
+ try {
+ List<RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == pid && proc.uid == uid
+ && proc.importance == IMPORTANCE_FOREGROUND) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
+
+ /**
+ * @param opPackageName name of package for caller
+ * @param requireForeground only allow this call while app is in the foreground
+ * @return true if caller can use face API
+ */
+ private boolean canUseFace(String opPackageName, boolean requireForeground, int uid,
+ int pid, int userId) {
+ checkPermission(USE_BIOMETRIC);
+ if (isKeyguard(opPackageName)) {
+ return true; // Keyguard is always allowed
+ }
+ if (!isCurrentUserOrProfile(userId)) {
+ Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
+ return false;
+ }
+ if (mAppOps.noteOp(AppOpsManager.OP_USE_FACE, uid, opPackageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
+ return false;
+ }
+ if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param opPackageName package of the caller
+ * @return true if this is the same client currently using face
+ */
+ private boolean currentClient(String opPackageName) {
+ return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+ }
+
+ /**
+ * @param clientPackage
+ * @return true if this is keyguard package
+ */
+ private boolean isKeyguard(String clientPackage) {
+ return mKeyguardPackage.equals(clientPackage);
+ }
+
+ private void addLockoutResetMonitor(FaceServiceLockoutResetMonitor monitor) {
+ if (!mLockoutMonitors.contains(monitor)) {
+ mLockoutMonitors.add(monitor);
+ }
+ }
+
+ private void removeLockoutResetCallback(
+ FaceServiceLockoutResetMonitor monitor) {
+ mLockoutMonitors.remove(monitor);
+ }
+
+ private void notifyLockoutResetMonitors() {
+ for (int i = 0; i < mLockoutMonitors.size(); i++) {
+ mLockoutMonitors.get(i).sendLockoutReset();
+ }
+ }
+
+ private void startAuthentication(IBinder token, long opId, int callingUserId,
+ IFaceServiceReceiver receiver, int flags, boolean restricted,
+ String opPackageName) {
+ updateActiveGroup(callingUserId, opPackageName);
+
+ if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
+
+ AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
+ receiver, mCurrentUserId, opId, restricted, opPackageName) {
+ @Override
+ public int handleFailedAttempt() {
+ mFailedAttempts++;
+ mTimedLockoutCleared = false;
+ final int lockoutMode = getLockoutMode();
+ if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+ mPerformanceStats.permanentLockout++;
+ } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+ mPerformanceStats.lockout++;
+ }
+
+ // Failing multiple times will continue to push out the lockout time
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ scheduleLockoutReset();
+ return lockoutMode;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
+ @Override
+ public void resetFailedAttempts() {
+ FaceService.this.resetFailedAttempts(true /* clearAttemptCounter */);
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ FaceService.this.userActivity();
+ }
+
+ @Override
+ public IBiometricsFace getFaceDaemon() {
+ return FaceService.this.getFaceDaemon();
+ }
+ };
+
+ int lockoutMode = getLockoutMode();
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(TAG, "In lockout mode(" + lockoutMode +
+ ") ; disallowing authentication");
+ int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
+ FaceManager.FACE_ERROR_LOCKOUT :
+ FaceManager.FACE_ERROR_LOCKOUT_PERMANENT;
+ if (!client.onError(errorCode, 0 /* vendorCode */)) {
+ Slog.w(TAG, "Cannot send permanent lockout message to client");
+ }
+ return;
+ }
+ startClient(client, true /* initiatedByClient */);
+ }
+
+ private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
+ IFaceServiceReceiver receiver, int flags, boolean restricted,
+ String opPackageName) {
+ updateActiveGroup(userId, opPackageName);
+
+ EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
+ userId, cryptoToken, restricted, opPackageName) {
+
+ @Override
+ public IBiometricsFace getFaceDaemon() {
+ return FaceService.this.getFaceDaemon();
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ FaceService.this.userActivity();
+ }
+ };
+ startClient(client, true /* initiatedByClient */);
+ }
+
+ // attempt counter should only be cleared when Keyguard goes away or when
+ // a face is successfully authenticated
+ protected void resetFailedAttempts(boolean clearAttemptCounter) {
+ if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(TAG, "Reset face lockout, clearAttemptCounter=" + clearAttemptCounter);
+ }
+ if (clearAttemptCounter) {
+ mFailedAttempts = 0;
+ }
+ mTimedLockoutCleared = true;
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+ // the alarm might still be pending; remove it.
+ cancelLockoutReset();
+ notifyLockoutResetMonitors();
+ }
+
+ private class FaceServiceLockoutResetMonitor {
+
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+ private final IFaceServiceLockoutResetCallback mCallback;
+ private final WakeLock mWakeLock;
+
+ public FaceServiceLockoutResetMonitor(
+ IFaceServiceLockoutResetCallback callback) {
+ mCallback = callback;
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "lockout reset callback");
+ }
+
+ public void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(mHalDeviceId, new IRemoteCallback.Stub() {
+
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ });
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
+ mHandler.post(mRemoveCallbackRunnable);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+ }
+ }
+ }
+
+ private final Runnable mRemoveCallbackRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ removeLockoutResetCallback(FaceServiceLockoutResetMonitor.this);
+ }
+ };
+ }
+
+ private IBiometricsFaceClientCallback mDaemonCallback =
+ new IBiometricsFaceClientCallback.Stub() {
+
+ @Override
+ public void onEnrollResult(final long deviceId, int faceId, int userId, int remaining) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleEnrollResult(deviceId, faceId, userId, remaining);
+ }
+ });
+ }
+
+ @Override
+ public void onAcquired(final long deviceId, final int userId, final int acquiredInfo,
+ final int vendorCode) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleAcquired(deviceId, acquiredInfo, vendorCode);
+ }
+ });
+ }
+
+ @Override
+ public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+ ArrayList<Byte> token) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleAuthenticated(deviceId, faceId, userId, token);
+ }
+ });
+ }
+
+ @Override
+ public void onError(final long deviceId, final int userId, final int error,
+ final int vendorCode) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleError(deviceId, error, vendorCode);
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(final long deviceId, final int faceId, final int userId,
+ final int remaining) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ handleRemoved(deviceId, faceId, userId, remaining);
+ }
+ });
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+ throws RemoteException {
+
+ }
+ };
+
+ private final class FaceServiceWrapper extends IFaceService.Stub {
+ @Override // Binder call
+ public long preEnroll(IBinder token) {
+ checkPermission(MANAGE_FACE);
+ return startPreEnroll(token);
+ }
+
+ @Override // Binder call
+ public int postEnroll(IBinder token) {
+ checkPermission(MANAGE_FACE);
+ return startPostEnroll(token);
+ }
+
+ @Override // Binder call
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+ final IFaceServiceReceiver receiver, final int flags,
+ final String opPackageName) {
+ checkPermission(MANAGE_FACE);
+
+ Face enrolledFace = FaceService.this.getEnrolledFace(userId);
+
+ if (enrolledFace != null) {
+ Slog.w(TAG, "Multiple faces enrollment is not supported");
+ return;
+ }
+
+
+ final boolean restricted = isRestricted();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ startEnrollment(token, cryptoToken, userId, receiver, flags,
+ restricted, opPackageName);
+ }
+ });
+ }
+
+ private boolean isRestricted() {
+ // Only give privileged apps (like Settings) access to faces info
+ final boolean restricted = !hasPermission(MANAGE_FACE);
+ return restricted;
+ }
+
+ @Override // Binder call
+ public void cancelEnrollment(final IBinder token) {
+ checkPermission(MANAGE_FACE);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof EnrollClient && client.getToken() == token) {
+ client.stop(client.getToken() == token);
+ }
+ }
+ });
+ }
+
+ @Override // Binder call
+ public void authenticate(final IBinder token, final long opId,
+ final IFaceServiceReceiver receiver, final int flags,
+ final String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int pid = Binder.getCallingPid();
+ final boolean restricted = isRestricted();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (!canUseFace(opPackageName, true /* foregroundOnly */,
+ callingUid, pid, callingUserId)) {
+ if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
+ return;
+ }
+
+ MetricsLogger.histogram(mContext, "faces_token", opId != 0L ? 1 : 0);
+
+ // Get performance stats object for this user.
+ HashMap<Integer, PerformanceStats> pmap
+ = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+ PerformanceStats stats = pmap.get(mCurrentUserId);
+ if (stats == null) {
+ stats = new PerformanceStats();
+ pmap.put(mCurrentUserId, stats);
+ }
+ mPerformanceStats = stats;
+
+ startAuthentication(token, opId, callingUserId, receiver,
+ flags, restricted, opPackageName);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (!canUseFace(opPackageName, true /* foregroundOnly */, uid, pid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
+ } else {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof AuthenticationClient) {
+ if (client.getToken() == token) {
+ if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
+ client.stop(client.getToken() == token);
+ } else {
+ if (DEBUG) Slog.v(TAG, "can't stop client "
+ + client.getOwnerString() + " since tokens don't match");
+ }
+ } else if (client != null) {
+ if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
+ + client.getOwnerString());
+ }
+ }
+ }
+ });
+ }
+
+ @Override // Binder call
+ public void setActiveUser(final int userId) {
+ checkPermission(MANAGE_FACE);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateActiveGroup(userId, null);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public void remove(final IBinder token, final int userId,
+ final IFaceServiceReceiver receiver) {
+ checkPermission(MANAGE_FACE); // TODO: Maybe have another permission
+ final boolean restricted = isRestricted();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ startRemove(token, userId, receiver, restricted, false /* internal */);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ if (!canUseFace(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IBiometricsFace daemon = getFaceDaemon();
+ return daemon != null && mHalDeviceId != 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public Face getEnrolledFace(int userId, String opPackageName) {
+ if (!canUseFace(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return null;
+ }
+
+ return FaceService.this.getEnrolledFace(userId);
+ }
+
+ @Override // Binder call
+ public boolean hasEnrolledFace(int userId, String opPackageName) {
+ if (!canUseFace(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ return FaceService.this.hasEnrolledFace(userId);
+ }
+
+ @Override // Binder call
+ public long getAuthenticatorId(String opPackageName) {
+ // In this method, we're not checking whether the caller is permitted to use face
+ // API because current authenticator ID is leaked (in a more contrived way) via Android
+ // Keystore (android.security.keystore package): the user of that API can create a key
+ // which requires face authentication for its use, and then query the key's
+ // characteristics (hidden API) which returns, among other things, face
+ // authenticator ID which was active at key creation time.
+ //
+ // Reason: The part of Android Keystore which runs inside an app's process invokes this
+ // method in certain cases. Those cases are not always where the developer demonstrates
+ // explicit intent to use face functionality. Thus, to avoiding throwing an
+ // unexpected SecurityException this method does not check whether its caller is
+ // permitted to use face API.
+ //
+ // The permission check should be restored once Android Keystore no longer invokes this
+ // method from inside app processes.
+
+ return FaceService.this.getAuthenticatorId(opPackageName);
+ }
+
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInternal(pw);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void resetTimeout(byte [] token) {
+ checkPermission(RESET_FACE_LOCKOUT);
+ // TODO: confirm security token when we move timeout management into the HAL layer.
+ mHandler.post(mResetFailedAttemptsRunnable);
+ }
+
+ @Override
+ public void addLockoutResetCallback(final IFaceServiceLockoutResetCallback callback)
+ throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ addLockoutResetMonitor(
+ new FaceServiceLockoutResetMonitor(callback));
+ }
+ });
+ }
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Face Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ PerformanceStats stats = mPerformanceMap.get(userId);
+ PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("accept", (stats != null) ? stats.accept : 0);
+ set.put("reject", (stats != null) ? stats.reject : 0);
+ set.put("acquire", (stats != null) ? stats.acquire : 0);
+ set.put("lockout", (stats != null) ? stats.lockout : 0);
+ set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+ // cryptoStats measures statistics about secure face transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+ set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+ set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+ set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(FaceServiceDumpProto.USERS);
+
+ proto.write(FaceUserStatsProto.USER_ID, userId);
+
+ // Normal face authentications (e.g. lockscreen)
+ final PerformanceStats normal = mPerformanceMap.get(userId);
+ if (normal != null) {
+ final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
+ proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
+ proto.write(FaceActionStatsProto.REJECT, normal.reject);
+ proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
+ proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
+ proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
+ proto.end(countsToken);
+ }
+
+ // Statistics about secure face transactions (e.g. to unlock password
+ // storage, make secure purchases, etc.)
+ final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+ if (crypto != null) {
+ final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
+ proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
+ proto.write(FaceActionStatsProto.REJECT, crypto.reject);
+ proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
+ proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
+ proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
+ proto.end(countsToken);
+ }
+
+ proto.end(userToken);
+ }
+ proto.flush();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
+ SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
+ listenForUserSwitches();
+ }
+
+ private void updateActiveGroup(int userId, String clientPackage) {
+ IBiometricsFace daemon = getFaceDaemon();
+
+ if (daemon != null) {
+ try {
+ userId = getUserOrWorkProfileId(clientPackage, userId);
+ if (userId != mCurrentUserId) {
+ final File systemDir = Environment.getUserSystemDirectory(userId);
+ final File faceDir = new File(systemDir, FACE_DATA_DIR);
+ if (!faceDir.exists()) {
+ if (!faceDir.mkdir()) {
+ Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
+ return;
+ }
+ // Calling mkdir() from this process will create a directory with our
+ // permissions (inherited from the containing dir). This command fixes
+ // the label.
+ if (!SELinux.restorecon(faceDir)) {
+ Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+ return;
+ }
+ }
+ daemon.setActiveUser(userId, faceDir.getAbsolutePath());
+ mCurrentUserId = userId;
+ }
+ mAuthenticatorIds.put(userId,
+ hasEnrolledFace(userId) ? daemon.getAuthenticatorId().value : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveUser():", e);
+ }
+ }
+ }
+
+ /**
+ * @param clientPackage the package of the caller
+ * @return the profile id
+ */
+ private int getUserOrWorkProfileId(String clientPackage, int userId) {
+ if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+ return userId;
+ }
+ return getEffectiveUserId(userId);
+ }
+
+ /**
+ * @param userId
+ * @return true if this is a work profile
+ */
+ private boolean isWorkProfile(int userId) {
+ UserInfo userInfo = null;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ userInfo = mUserManager.getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return userInfo != null && userInfo.isManagedProfile();
+ }
+
+ private void listenForUserSwitches() {
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) throws RemoteException {
+ mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+ .sendToTarget();
+ }
+ }, TAG);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to listen for user switching event" ,e);
+ }
+ }
+
+ /***
+ * @param opPackageName the name of the calling package
+ * @return authenticator id for the calling user
+ */
+ public long getAuthenticatorId(String opPackageName) {
+ final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
+ return mAuthenticatorIds.getOrDefault(userId, 0L);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
new file mode 100644
index 0000000..45b842d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
@@ -0,0 +1,191 @@
+/*
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.face.Face;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+
+/**
+ * Class managing the set of faces per user across device reboots.
+ */
+class FaceUserState {
+
+ private static final String TAG = "FaceState";
+ private static final String FACE_FILE = "settings_face.xml";
+
+ private static final String TAG_FACE = "face";
+ private static final String ATTR_DEVICE_ID = "deviceId";
+
+ private final File mFile;
+
+ @GuardedBy("this")
+ private Face mFace = null;
+ private final Context mCtx;
+
+ public FaceUserState(Context ctx, int userId) {
+ mFile = getFileForUser(userId);
+ mCtx = ctx;
+ synchronized (this) {
+ readStateSyncLocked();
+ }
+ }
+
+ public void addFace(int faceId) {
+ synchronized (this) {
+ mFace = new Face("Face", faceId, 0);
+ scheduleWriteStateLocked();
+ }
+ }
+
+ public void removeFace() {
+ synchronized (this) {
+ mFace = null;
+ scheduleWriteStateLocked();
+ }
+ }
+
+ public Face getFace() {
+ synchronized (this) {
+ return getCopy(mFace);
+ }
+ }
+
+ private static File getFileForUser(int userId) {
+ return new File(Environment.getUserSystemDirectory(userId), FACE_FILE);
+ }
+
+ private final Runnable mWriteStateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ doWriteState();
+ }
+ };
+
+ private void scheduleWriteStateLocked() {
+ AsyncTask.execute(mWriteStateRunnable);
+ }
+
+ private Face getCopy(Face f) {
+ if (f == null) {
+ return null;
+ }
+ return new Face(f.getName(), f.getFaceId(), f.getDeviceId());
+ }
+
+ private void doWriteState() {
+ AtomicFile destination = new AtomicFile(mFile);
+
+ Face face;
+
+ synchronized (this) {
+ face = getCopy(mFace);
+ }
+
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_FACE);
+ if (face != null) {
+ serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(face.getDeviceId()));
+ }
+ serializer.endTag(null, TAG_FACE);
+ serializer.endDocument();
+ destination.finishWrite(out);
+
+ // Any error while writing is fatal.
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+ destination.failWrite(out);
+ throw new IllegalStateException("Failed to write face", t);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private void readStateSyncLocked() {
+ FileInputStream in;
+ if (!mFile.exists()) {
+ return;
+ }
+ try {
+ in = new FileInputStream(mFile);
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(TAG, "No face state");
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseStateLocked(parser);
+
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed parsing settings file: "
+ + mFile , e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void parseStateLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_FACE)) {
+ parseFaceLocked(parser);
+ }
+ }
+ }
+
+ private void parseFaceLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+
+ mFace = new Face("", 0, Integer.parseInt(deviceId));
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
new file mode 100644
index 0000000..c4a8499
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
@@ -0,0 +1,71 @@
+/**
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.face.Face;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Utility class for dealing with faces and face settings.
+ */
+public class FaceUtils {
+
+ private static final Object sInstanceLock = new Object();
+ private static FaceUtils sInstance;
+
+ @GuardedBy("this")
+ private final SparseArray<FaceUserState> mUsers = new SparseArray<>();
+
+ public static FaceUtils getInstance() {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new FaceUtils();
+ }
+ }
+ return sInstance;
+ }
+
+ private FaceUtils() {
+ }
+
+ public Face getFaceForUser(Context ctx, int userId) {
+ return getStateForUser(ctx, userId).getFace();
+ }
+
+ public void addFaceForUser(Context ctx, int faceId, int userId) {
+ getStateForUser(ctx, userId).addFace(faceId);
+ }
+
+ public void removeFaceForUser(Context ctx, int userId) {
+ getStateForUser(ctx, userId).removeFace();
+ }
+
+ private FaceUserState getStateForUser(Context ctx, int userId) {
+ synchronized (this) {
+ FaceUserState state = mUsers.get(userId);
+ if (state == null) {
+ state = new FaceUserState(ctx, userId);
+ mUsers.put(userId, state);
+ }
+ return state;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java b/services/core/java/com/android/server/biometrics/face/InternalRemovalClient.java
similarity index 63%
rename from services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
rename to services/core/java/com/android/server/biometrics/face/InternalRemovalClient.java
index 19f61fe..634bf8b 100644
--- a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/face/InternalRemovalClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,19 @@
* limitations under the License
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.face;
import android.content.Context;
import android.os.IBinder;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import com.android.server.fingerprint.RemovalClient;
+import android.hardware.face.IFaceServiceReceiver;
+import com.android.server.biometrics.face.RemovalClient;
public abstract class InternalRemovalClient extends RemovalClient {
public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
- boolean restricted, String owner) {
+ IFaceServiceReceiver receiver, int userId, boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, fingerId, groupId, userId, restricted, owner);
+ super(context, halDeviceId, token, receiver, userId, restricted, owner);
}
}
diff --git a/services/core/java/com/android/server/biometrics/face/RemovalClient.java b/services/core/java/com/android/server/biometrics/face/RemovalClient.java
new file mode 100644
index 0000000..ccea9d5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/RemovalClient.java
@@ -0,0 +1,115 @@
+/**
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+
+ public RemovalClient(Context context, long halDeviceId, IBinder token,
+ IFaceServiceReceiver receiver, int userId,
+ boolean restricted, String owner) {
+ super(context, halDeviceId, token, receiver, userId, restricted, owner);
+ }
+
+ @Override
+ public int start() {
+ IBiometricsFace daemon = getFaceDaemon();
+ // The face template ids will be removed when we get confirmation from the HAL
+ try {
+ final int result = daemon.remove(getTargetUserId());
+ if (result != 0) {
+ Slog.w(TAG, "startRemove failed, result=" + result);
+ MetricsLogger.histogram(getContext(), "faced_remove_start_error", result);
+ onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startRemove failed", e);
+ }
+ return 0;
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(TAG, "stopRemove: already cancelled!");
+ return 0;
+ }
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "stopRemoval: no face HAL!");
+ return ERROR_ESRCH;
+ }
+ try {
+ final int result = daemon.cancel();
+ if (result != 0) {
+ Slog.w(TAG, "stopRemoval failed, result=" + result);
+ return result;
+ }
+ if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "stopRemoval failed", e);
+ return ERROR_ESRCH;
+ }
+ mAlreadyCancelled = true;
+ return 0; // success
+ }
+
+ /*
+ * @return true if we're done.
+ */
+ private boolean sendRemoved(int faceId, int remaining) {
+ IFaceServiceReceiver receiver = getReceiver();
+ try {
+ if (receiver != null) {
+ receiver.onRemoved(getHalDeviceId(), faceId, remaining);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify Removed:", e);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onRemoved(int faceId, int remaining) {
+ FaceUtils.getInstance().removeFaceForUser(getContext(), getTargetUserId());
+ return sendRemoved(faceId, remaining);
+ }
+
+ @Override
+ public boolean onEnrollResult(int faceId, int remaining) {
+ if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
+ return true; // Invalid for Remove
+ }
+
+ @Override
+ public boolean onAuthenticated(int faceId) {
+ if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
+ return true; // Invalid for Remove.
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
new file mode 100644
index 0000000..ba8b3b3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.common.Metrics;
+
+public class FingerprintMetrics implements Metrics {
+
+ @Override
+ public String logTag() {
+ return FingerprintService.TAG;
+ }
+
+ @Override
+ public String tagHalDied() {
+ return "fingerprintd_died";
+ }
+
+ @Override
+ public String tagAuthToken() {
+ return "fingerprint_token";
+ }
+
+ @Override
+ public String tagAuthStartError() {
+ return "fingerprintd_auth_start_error";
+ }
+
+ @Override
+ public String tagEnrollStartError() {
+ return "fingerprintd_enroll_start_error";
+ }
+
+ @Override
+ public String tagEnumerateStartError() {
+ return "fingerprintd_enum_start_error";
+ }
+
+ @Override
+ public String tagRemoveStartError() {
+ return "fingerprintd_remove_start_error";
+ }
+
+ @Override
+ public int actionBiometricAuth() {
+ return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_AUTH;
+ }
+
+ @Override
+ public int actionBiometricEnroll() {
+ return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..9397418
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -0,0 +1,1130 @@
+/*
+ * 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.
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_FINGERPRINT;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.common.BiometricService;
+import com.android.server.biometrics.common.ClientMonitor;
+import com.android.server.biometrics.common.EnumerateClient;
+import com.android.server.biometrics.common.Metrics;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint-related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends BiometricService {
+
+ protected static final String TAG = "FingerprintService";
+ private static final boolean DEBUG = true;
+ private static final boolean CLEANUP_UNUSED_FP = true;
+ private static final String FP_DATA_DIR = "fpdata";
+ private static final String ACTION_LOCKOUT_RESET =
+ "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET";
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+
+ private final class UserFingerprint {
+ Fingerprint f;
+ int userId;
+ public UserFingerprint(Fingerprint f, int userId) {
+ this.f = f;
+ this.userId = userId;
+ }
+ }
+
+ /**
+ * Receives the incoming binder calls from FingerprintManager.
+ */
+ private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+
+ /**
+ * The following methods contain common code which is shared in biometrics/common.
+ */
+
+ @Override // Binder call
+ public long preEnroll(IBinder token) {
+ checkPermission(MANAGE_FINGERPRINT);
+ return startPreEnroll(token);
+ }
+
+ @Override // Binder call
+ public int postEnroll(IBinder token) {
+ checkPermission(MANAGE_FINGERPRINT);
+ return startPostEnroll(token);
+ }
+
+ @Override // Binder call
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+ final IFingerprintServiceReceiver receiver, final int flags,
+ final String opPackageName) {
+ checkPermission(MANAGE_FINGERPRINT);
+
+ final boolean restricted = isRestricted();
+ final int groupId = userId; // default group for fingerprint enrollment
+ final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
+ cryptoToken, restricted, opPackageName);
+
+
+ enrollInternal(client, userId);
+ }
+
+ @Override // Binder call
+ public void cancelEnrollment(final IBinder token) {
+ checkPermission(MANAGE_FINGERPRINT);
+ cancelEnrollmentInternal(token);
+ }
+
+ @Override // Binder call
+ public void authenticate(final IBinder token, final long opId, final int groupId,
+ final IFingerprintServiceReceiver receiver, final int flags,
+ final String opPackageName, final Bundle bundle,
+ final IBiometricPromptReceiver dialogReceiver) {
+ final boolean restricted = isRestricted();
+ final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+ mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
+ dialogReceiver, mStatusBarService) {
+
+ };
+ authenticateInternal(client, opId, opPackageName);
+ }
+
+ @Override // Binder call
+ public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ cancelAuthenticationInternal(token, opPackageName);
+ }
+
+ @Override // Binder call
+ public void setActiveUser(final int userId) {
+ checkPermission(MANAGE_FINGERPRINT);
+ setActiveUserInternal(userId);
+ }
+
+ @Override // Binder call
+ public void remove(final IBinder token, final int fingerId, final int groupId,
+ final int userId, final IFingerprintServiceReceiver receiver) {
+ checkPermission(MANAGE_FINGERPRINT);
+
+ if (token == null) {
+ Slog.w(TAG, "remove(): token is null");
+ return;
+ }
+
+ final boolean restricted = isRestricted();
+ final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
+ userId, restricted, token.toString());
+ client.setShouldNotifyUserActivity(true);
+ removeInternal(client);
+ }
+
+ @Override // Binder call
+ public void enumerate(final IBinder token, final int userId,
+ final IFingerprintServiceReceiver receiver) {
+ checkPermission(MANAGE_FINGERPRINT);
+
+ final boolean restricted = isRestricted();
+ final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
+ restricted, getContext().getOpPackageName());
+ enumerateInternal(client);
+ }
+
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInternal(pw);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * The following methods don't use any common code from BiometricService
+ */
+
+ @Override // Binder call
+ public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ return daemon != null && mHalDeviceId != 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void rename(final int fingerId, final int groupId, final String name) {
+ checkPermission(MANAGE_FINGERPRINT);
+ if (!isCurrentUserOrProfile(groupId)) {
+ return;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mFingerprintUtils.renameFingerprintForUser(mContext, fingerId,
+ groupId, name);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return Collections.emptyList();
+ }
+
+ return FingerprintService.this.getEnrolledFingerprints(userId);
+ }
+
+ @Override // Binder call
+ public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ return FingerprintService.this.hasEnrolledBiometrics(userId);
+ }
+
+ @Override // Binder call
+ public long getAuthenticatorId(String opPackageName) {
+ // In this method, we're not checking whether the caller is permitted to use fingerprint
+ // API because current authenticator ID is leaked (in a more contrived way) via Android
+ // Keystore (android.security.keystore package): the user of that API can create a key
+ // which requires fingerprint authentication for its use, and then query the key's
+ // characteristics (hidden API) which returns, among other things, fingerprint
+ // authenticator ID which was active at key creation time.
+ //
+ // Reason: The part of Android Keystore which runs inside an app's process invokes this
+ // method in certain cases. Those cases are not always where the developer demonstrates
+ // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
+ // unexpected SecurityException this method does not check whether its caller is
+ // permitted to use fingerprint API.
+ //
+ // The permission check should be restored once Android Keystore no longer invokes this
+ // method from inside app processes.
+
+ return FingerprintService.this.getAuthenticatorId(opPackageName);
+ }
+
+ @Override // Binder call
+ public void resetTimeout(byte [] token) {
+ checkPermission(RESET_FINGERPRINT_LOCKOUT);
+ // TODO: confirm security token when we move timeout management into the HAL layer.
+ mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+ }
+
+ @Override
+ public void addLockoutResetCallback(final IFingerprintServiceLockoutResetCallback callback)
+ throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ addLockoutResetMonitor(
+ new FingerprintServiceLockoutResetMonitor(callback));
+ }
+ });
+ }
+
+ @Override
+ public boolean isClientActive() {
+ checkPermission(MANAGE_FINGERPRINT);
+ synchronized(FingerprintService.this) {
+ return (getCurrentClient() != null) || (getPendingClient() != null);
+ }
+ }
+
+ @Override
+ public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
+ checkPermission(MANAGE_FINGERPRINT);
+ mClientActiveCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
+ checkPermission(MANAGE_FINGERPRINT);
+ mClientActiveCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+ * the FingerprintManager.
+ */
+ private class ServiceListenerImpl implements ServiceListener {
+
+ private IFingerprintServiceReceiver mFingerprintServiceReceiver;
+
+ public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
+ mFingerprintServiceReceiver = receiver;
+ }
+
+ @Override
+ public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onEnrollResult(deviceId, fingerId, groupId, remaining);
+ }
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.BiometricIdentifier biometric, int userId)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver
+ .onAuthenticationSucceeded(deviceId, (Fingerprint)biometric, userId);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onAuthenticationFailed(deviceId);
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
+ }
+ }
+
+ @Override
+ public void onRemoved(long deviceId, int fingerId, int groupId, int remaining)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onRemoved(deviceId, fingerId, groupId, remaining);
+ }
+ }
+
+ @Override
+ public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onEnumerated(deviceId, fingerId, groupId, remaining);
+ }
+ }
+ }
+
+ private final class FingerprintServiceLockoutResetMonitor implements IBinder.DeathRecipient {
+
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+ private final IFingerprintServiceLockoutResetCallback mCallback;
+ private final WakeLock mWakeLock;
+
+ public FingerprintServiceLockoutResetMonitor(
+ IFingerprintServiceLockoutResetCallback callback) {
+ mCallback = callback;
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "lockout reset callback");
+ try {
+ mCallback.asBinder().linkToDeath(FingerprintServiceLockoutResetMonitor.this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath", e);
+ }
+ }
+
+ public void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(mHalDeviceId, new IRemoteCallback.Stub() {
+
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ releaseWakelock();
+ }
+ });
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
+ mHandler.post(mRemoveCallbackRunnable);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+ releaseWakelock();
+ }
+ }
+ }
+
+ private final Runnable mRemoveCallbackRunnable = new Runnable() {
+ @Override
+ public void run() {
+ releaseWakelock();
+ removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
+ }
+ };
+
+ @Override
+ public void binderDied() {
+ Slog.e(TAG, "Lockout reset callback binder died");
+ mHandler.post(mRemoveCallbackRunnable);
+ }
+
+ private void releaseWakelock() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ /**
+ * An internal class to help clean up unknown fingerprints in the hardware and software.
+ */
+ private final class InternalEnumerateClient extends BiometricService.EnumerateClientImpl {
+
+ private List<Fingerprint> mEnrolledList;
+ private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
+
+ public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int groupId, int userId,
+ boolean restricted, String owner, List<Fingerprint> enrolledList) {
+ super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted,
+ owner);
+ mEnrolledList = enrolledList;
+ }
+
+ private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {
+ boolean matched = false;
+ for (int i = 0; i < mEnrolledList.size(); i++) {
+ if (mEnrolledList.get(i).getFingerId() == fingerId) {
+ mEnrolledList.remove(i);
+ matched = true;
+ break;
+ }
+ }
+
+ // fingerId 0 means no fingerprints are in hardware
+ if (!matched && fingerId != 0) {
+ Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
+ mUnknownFingerprints.add(fingerprint);
+ }
+ }
+
+ private void doFingerprintCleanup() {
+ if (mEnrolledList == null) {
+ return;
+ }
+
+ for (Fingerprint f : mEnrolledList) {
+ Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: "
+ + f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
+ + " " + f.getDeviceId());
+ FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
+ f.getFingerId(), getTargetUserId());
+ }
+ mEnrolledList.clear();
+ }
+
+ public List<Fingerprint> getUnknownFingerprints() {
+ return mUnknownFingerprints;
+ }
+
+ @Override
+ public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
+ handleEnumeratedFingerprint(fingerId, groupId, remaining);
+ if (remaining == 0) {
+ doFingerprintCleanup();
+ }
+ return remaining == 0;
+ }
+ }
+
+ /**
+ * An internal class to help clean up unknown fingerprints in hardware and software.
+ */
+ private final class InternalRemovalClient extends BiometricService.RemovalClientImpl {
+ public InternalRemovalClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
+ String owner) {
+ super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId,
+ restricted,
+ owner);
+ }
+ }
+
+ private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
+ private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
+ new ArrayList<>();
+ private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+ new CopyOnWriteArrayList<>();
+ private final Map<Integer, Long> mAuthenticatorIds =
+ Collections.synchronizedMap(new HashMap<>());
+ private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
+ private final PowerManager mPowerManager;
+
+ @GuardedBy("this")
+ private IBiometricsFingerprint mDaemon;
+
+ private Context mContext;
+ private long mHalDeviceId;
+ private IStatusBarService mStatusBarService;
+ private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
+ private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
+
+ /**
+ * Receives callbacks from the HAL.
+ */
+ private IBiometricsFingerprintClientCallback mDaemonCallback =
+ new IBiometricsFingerprintClientCallback.Stub() {
+ @Override
+ public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
+ final int remaining) {
+ mHandler.post(() -> {
+ FingerprintService.super.handleEnrollResult(deviceId, fingerId, groupId, remaining);
+ });
+ }
+
+ @Override
+ public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
+ mHandler.post(() -> {
+ FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ FingerprintService.super.handleAuthenticated(deviceId, fingerId, groupId, token);
+ });
+ }
+
+ @Override
+ public void onError(final long deviceId, final int error, final int vendorCode) {
+ mHandler.post(() -> {
+ ClientMonitor client = getCurrentClient();
+ if (client instanceof InternalRemovalClient
+ || client instanceof InternalEnumerateClient) {
+ clearEnumerateState();
+ }
+ FingerprintService.super.handleError(deviceId, error, vendorCode);
+
+ // TODO: this chunk of code should be common to all biometric services
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ // If we get HW_UNAVAILABLE, try to connect again later...
+ Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+ synchronized (this) {
+ mDaemon = null;
+ mHalDeviceId = 0;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(final long deviceId, final int fingerId, final int groupId,
+ final int remaining) {
+ mHandler.post(() -> {
+ ClientMonitor client = getCurrentClient();
+ FingerprintService.super.handleRemoved(deviceId, fingerId, groupId, remaining);
+ if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
+ cleanupUnknownFingerprints();
+ } else if (client instanceof InternalRemovalClient){
+ clearEnumerateState();
+ }
+ });
+ }
+
+ @Override
+ public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
+ final int remaining) {
+ mHandler.post(() -> {
+ // TODO: factor out common enumerate logic if possible
+ FingerprintService.this.handleEnumerate(deviceId, fingerId, groupId, remaining);
+ });
+
+ }
+ };
+
+ /**
+ * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
+ * can be shared between the multiple biometric services.
+ */
+ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
+ protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+ @Override
+ public int authenticate(long operationId, int groupId) throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "authenticate(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.authenticate(operationId, groupId);
+ }
+
+ @Override
+ public int cancel() throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "cancel(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.cancel();
+ }
+
+ @Override
+ public int remove(int groupId, int biometricId) throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "remove(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.remove(groupId, biometricId);
+ }
+
+ @Override
+ public int enumerate() throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enumerate(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.enumerate();
+ }
+
+ @Override
+ public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ Slog.v(TAG, "startEnroll()");
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enroll(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.enroll(cryptoToken, groupId, timeout);
+ }
+ };
+
+ public FingerprintService(Context context) {
+ super(context);
+ mContext = context;
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ // TODO: can this be retrieved from AuthenticationClient, or BiometricService?
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+ SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getFailedAttemptsLockoutTimed() {
+ return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
+ }
+
+ @Override
+ protected int getFailedAttemptsLockoutPermanent() {
+ return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
+ }
+
+ @Override
+ protected Metrics getMetrics() {
+ return mFingerprintMetrics;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit(int userId) {
+ final int limit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+ final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many fingerprints registered");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void updateActiveGroup(int userId, String clientPackage) {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+
+ if (daemon != null) {
+ try {
+ userId = getUserOrWorkProfileId(clientPackage, userId);
+ if (userId != mCurrentUserId) {
+ int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
+ if (firstSdkInt < Build.VERSION_CODES.BASE) {
+ Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
+ "at least VERSION_CODES.BASE");
+ }
+ File baseDir;
+ if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
+ baseDir = Environment.getUserSystemDirectory(userId);
+ } else {
+ baseDir = Environment.getDataVendorDeDirectory(userId);
+ }
+
+ File fpDir = new File(baseDir, FP_DATA_DIR);
+ if (!fpDir.exists()) {
+ if (!fpDir.mkdir()) {
+ Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
+ return;
+ }
+ // Calling mkdir() from this process will create a directory with our
+ // permissions (inherited from the containing dir). This command fixes
+ // the label.
+ if (!SELinux.restorecon(fpDir)) {
+ Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+ return;
+ }
+ }
+
+ daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
+ mCurrentUserId = userId;
+ }
+ mAuthenticatorIds.put(userId,
+ hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId() : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveGroup():", e);
+ }
+ }
+ }
+
+ @Override
+ protected String getLockoutResetIntent() {
+ return ACTION_LOCKOUT_RESET;
+ }
+
+ @Override
+ protected String getLockoutBroadcastPermission() {
+ return RESET_FINGERPRINT_LOCKOUT;
+ }
+
+ @Override
+ protected long getHalDeviceId() {
+ return mHalDeviceId;
+ }
+
+ @Override
+ protected void handleUserSwitching(int userId) {
+ if (getCurrentClient() instanceof InternalRemovalClient
+ || getCurrentClient() instanceof InternalEnumerateClient) {
+ Slog.w(TAG, "User switched while performing cleanup");
+ removeClient(getCurrentClient());
+ clearEnumerateState();
+ }
+ updateActiveGroup(userId, null);
+ doFingerprintCleanupForUser(userId);
+ }
+
+
+ @Override
+ protected boolean hasEnrolledBiometrics(int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ checkPermission(INTERACT_ACROSS_USERS);
+ }
+ return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
+ }
+
+ @Override
+ protected String getManageBiometricPermission() {
+ return MANAGE_FINGERPRINT;
+ }
+
+ @Override
+ protected void checkUseBiometricPermission() {
+ if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ != PackageManager.PERMISSION_GRANTED) {
+ checkPermission(USE_BIOMETRIC);
+ }
+ }
+
+ @Override
+ protected int getAppOp() {
+ return AppOpsManager.OP_USE_FINGERPRINT;
+ }
+
+ @Override
+ protected void notifyLockoutResetMonitors() {
+ for (int i = 0; i < mLockoutMonitors.size(); i++) {
+ mLockoutMonitors.get(i).sendLockoutReset();
+ }
+ }
+
+ @Override
+ protected void notifyClientActiveCallbacks(boolean isActive) {
+ List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ callbacks.get(i).onClientActiveChanged(isActive);
+ } catch (RemoteException re) {
+ // If the remote is dead, stop notifying it
+ mClientActiveCallbacks.remove(callbacks.get(i));
+ }
+ }
+ }
+
+ /** Gets the fingerprint daemon */
+ private synchronized IBiometricsFingerprint getFingerprintDaemon() {
+ if (mDaemon == null) {
+ Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
+ try {
+ mDaemon = IBiometricsFingerprint.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened. Logged below.
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get biometric interface", e);
+ }
+ if (mDaemon == null) {
+ Slog.w(TAG, "fingerprint HIDL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0);
+
+ try {
+ mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to open fingerprint HAL", e);
+ mDaemon = null; // try again later!
+ }
+
+ if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
+ if (mHalDeviceId != 0) {
+ loadAuthenticatorIds();
+ updateActiveGroup(ActivityManager.getCurrentUser(), null);
+ doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
+ } else {
+ Slog.w(TAG, "Failed to open Fingerprint HAL!");
+ MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
+ mDaemon = null;
+ }
+ }
+ return mDaemon;
+ }
+
+ /** Populates existing authenticator ids. To be used only during the start of the service. */
+ private void loadAuthenticatorIds() {
+ // This operation can be expensive, so keep track of the elapsed time. Might need to move to
+ // background if it takes too long.
+ long t = System.currentTimeMillis();
+ mAuthenticatorIds.clear();
+ for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+ int userId = getUserOrWorkProfileId(null, user.id);
+ if (!mAuthenticatorIds.containsKey(userId)) {
+ updateActiveGroup(userId, null);
+ }
+ }
+
+ t = System.currentTimeMillis() - t;
+ if (t > 1000) {
+ Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
+ }
+ }
+
+ /**
+ * This method should be called upon connection to the daemon, and when user switches.
+ * @param userId
+ */
+ private void doFingerprintCleanupForUser(int userId) {
+ if (CLEANUP_UNUSED_FP) {
+ enumerateUser(userId);
+ }
+ }
+
+ private void clearEnumerateState() {
+ if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
+ mUnknownFingerprints.clear();
+ }
+
+ private void enumerateUser(int userId) {
+ if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
+
+ final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+ final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+
+ InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper,
+ mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted,
+ getContext().getOpPackageName(), enrolledList);
+ enumerateInternal(client);
+ }
+
+ // Remove unknown fingerprints from hardware
+ private void cleanupUnknownFingerprints() {
+ if (!mUnknownFingerprints.isEmpty()) {
+ UserFingerprint uf = mUnknownFingerprints.get(0);
+ mUnknownFingerprints.remove(uf);
+ boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+ InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper,
+ mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getFingerId(),
+ uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName());
+ removeInternal(client);
+ } else {
+ clearEnumerateState();
+ }
+ }
+
+ private void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
+ ClientMonitor client = getCurrentClient();
+
+ if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
+ return;
+ }
+ client.onEnumerationResult(fingerId, groupId, remaining);
+
+ // All fingerprints in hardware for this user were enumerated
+ if (remaining == 0) {
+ if (client instanceof InternalEnumerateClient) {
+ List<Fingerprint> unknownFingerprints =
+ ((InternalEnumerateClient) client).getUnknownFingerprints();
+
+ if (!unknownFingerprints.isEmpty()) {
+ Slog.w(TAG, "Adding " + unknownFingerprints.size() +
+ " fingerprints for deletion");
+ }
+ for (Fingerprint f : unknownFingerprints) {
+ mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
+ }
+ removeClient(client);
+ cleanupUnknownFingerprints();
+ } else {
+ removeClient(client);
+ }
+ }
+ }
+
+ private long startPreEnroll(IBinder token) {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
+ return 0;
+ }
+ try {
+ return daemon.preEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPreEnroll failed", e);
+ }
+ return 0;
+ }
+
+ private int startPostEnroll(IBinder token) {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
+ return 0;
+ }
+ try {
+ return daemon.postEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPostEnroll failed", e);
+ }
+ return 0;
+ }
+
+ private List<Fingerprint> getEnrolledFingerprints(int userId) {
+ return mFingerprintUtils.getFingerprintsForUser(mContext, userId);
+ }
+
+ private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
+ if (!mLockoutMonitors.contains(monitor)) {
+ mLockoutMonitors.add(monitor);
+ }
+ }
+
+ private void removeLockoutResetCallback(
+ FingerprintServiceLockoutResetMonitor monitor) {
+ mLockoutMonitors.remove(monitor);
+ }
+
+ /***
+ * @param opPackageName the name of the calling package
+ * @return authenticator id for the calling user
+ */
+ private long getAuthenticatorId(String opPackageName) {
+ final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
+ return mAuthenticatorIds.getOrDefault(userId, 0L);
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Fingerprint Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
+ PerformanceStats stats = mPerformanceMap.get(userId);
+ PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ set.put("accept", (stats != null) ? stats.accept : 0);
+ set.put("reject", (stats != null) ? stats.reject : 0);
+ set.put("acquire", (stats != null) ? stats.acquire : 0);
+ set.put("lockout", (stats != null) ? stats.lockout : 0);
+ set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+ // cryptoStats measures statistics about secure fingerprint transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+ set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+ set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+ set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+ set.put("permanentLockoutCrypto",
+ (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
+
+ proto.write(FingerprintUserStatsProto.USER_ID, userId);
+ proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
+ mFingerprintUtils.getFingerprintsForUser(mContext, userId).size());
+
+ // Normal fingerprint authentications (e.g. lockscreen)
+ final PerformanceStats normal = mPerformanceMap.get(userId);
+ if (normal != null) {
+ final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
+ proto.write(PerformanceStatsProto.ACCEPT, normal.accept);
+ proto.write(PerformanceStatsProto.REJECT, normal.reject);
+ proto.write(PerformanceStatsProto.ACQUIRE, normal.acquire);
+ proto.write(PerformanceStatsProto.LOCKOUT, normal.lockout);
+ proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, normal.permanentLockout);
+ proto.end(countsToken);
+ }
+
+ // Statistics about secure fingerprint transactions (e.g. to unlock password
+ // storage, make secure purchases, etc.)
+ final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+ if (crypto != null) {
+ final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
+ proto.write(PerformanceStatsProto.ACCEPT, crypto.accept);
+ proto.write(PerformanceStatsProto.REJECT, crypto.reject);
+ proto.write(PerformanceStatsProto.ACQUIRE, crypto.acquire);
+ proto.write(PerformanceStatsProto.LOCKOUT, crypto.lockout);
+ proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, crypto.permanentLockout);
+ proto.end(countsToken);
+ }
+
+ proto.end(userToken);
+ }
+ proto.flush();
+ mPerformanceMap.clear();
+ mCryptoPerformanceMap.clear();
+ }
+}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
similarity index 97%
rename from services/core/java/com/android/server/fingerprint/FingerprintUtils.java
rename to services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
index 5fbd735..cfb2b75 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.fingerprint;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintsUserState.java
similarity index 99%
rename from services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
rename to services/core/java/com/android/server/biometrics/fingerprint/FingerprintsUserState.java
index b0cde2d..2d9d87e 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintsUserState.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.fingerprint;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 19b61e6..c370959 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -166,14 +166,14 @@
/**
* Updates the PAC Manager with current Proxy information. This is called by
- * the ConnectivityService directly before a broadcast takes place to allow
+ * the ProxyTracker directly before a broadcast takes place to allow
* the PacManager to indicate that the broadcast should not be sent and the
* PacManager will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
* @return Returns true when the broadcast should not be sent
*/
- public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+ synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
// Allow to send broadcast, nothing to do.
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index daaedd8..dc65e1e 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -51,15 +51,25 @@
// possible
@NonNull
public final Object mProxyLock = new Object();
+ // The global proxy is the proxy that is set device-wide, overriding any network-specific
+ // proxy. Note however that proxies are hints ; the system does not enforce their use. Hence
+ // this value is only for querying.
@Nullable
@GuardedBy("mProxyLock")
public ProxyInfo mGlobalProxy = null;
+ // The default proxy is the proxy that applies to no particular network if the global proxy
+ // is not set. Individual networks have their own settings that override this. This member
+ // is set through setDefaultProxy, which is called when the default network changes proxies
+ // in its LinkProperties, or when ConnectivityService switches to a new default network, or
+ // when PacManager resolves the proxy.
@Nullable
@GuardedBy("mProxyLock")
public volatile ProxyInfo mDefaultProxy = null;
+ // Whether the default proxy is disabled. TODO : make this mDefaultProxyEnabled
@GuardedBy("mProxyLock")
public boolean mDefaultProxyDisabled = false;
+ // The object responsible for Proxy Auto Configuration (PAC).
@NonNull
private final PacManager mPacManager;
@@ -98,6 +108,16 @@
return Objects.equals(pa, pb) && (pa == null || Objects.equals(pa.getHost(), pb.getHost()));
}
+ /**
+ * Gets the default system-wide proxy.
+ *
+ * This will return the global proxy if set, otherwise the default proxy if in use. Note
+ * that this is not necessarily the proxy that any given process should use, as the right
+ * proxy for a process is the proxy for the network this process will use, which may be
+ * different from this value. This value is simply the default in case there is no proxy set
+ * in the network that will be used by a specific process.
+ * @return The default system-wide proxy or null if none.
+ */
@Nullable
public ProxyInfo getDefaultProxy() {
// This information is already available as a world read/writable jvm property.
@@ -108,6 +128,11 @@
}
}
+ /**
+ * Gets the global proxy.
+ *
+ * @return The global proxy or null if none.
+ */
@Nullable
public ProxyInfo getGlobalProxy() {
// This information is already available as a world read/writable jvm property.
@@ -116,19 +141,22 @@
}
}
- public boolean setCurrentProxyScriptUrl(@NonNull final ProxyInfo proxy) {
- return mPacManager.setCurrentProxyScriptUrl(proxy);
- }
-
- // TODO : make the argument NonNull final
- public void sendProxyBroadcast(@Nullable ProxyInfo proxy) {
- if (proxy == null) proxy = new ProxyInfo("", 0, "");
- if (setCurrentProxyScriptUrl(proxy)) return;
- if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxy);
+ /**
+ * Sends the system broadcast informing apps about a new proxy configuration.
+ *
+ * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
+ * to do in a "sendProxyBroadcast" method.
+ * @param proxyInfo the proxy spec, or null for no proxy.
+ */
+ // TODO : make the argument NonNull final and the method private
+ public void sendProxyBroadcast(@Nullable ProxyInfo proxyInfo) {
+ if (proxyInfo == null) proxyInfo = new ProxyInfo("", 0, "");
+ if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) return;
+ if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
+ intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxyInfo);
final long ident = Binder.clearCallingIdentity();
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
@@ -137,30 +165,39 @@
}
}
- public void setGlobalProxy(@Nullable ProxyInfo proxyProperties) {
+ /**
+ * Sets the global proxy in memory. Also writes the values to the global settings of the device.
+ *
+ * @param proxyInfo the proxy spec, or null for no proxy.
+ */
+ public void setGlobalProxy(@Nullable ProxyInfo proxyInfo) {
synchronized (mProxyLock) {
- if (proxyProperties == mGlobalProxy) return;
- if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
- if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
+ // ProxyInfo#equals is not commutative :( and is public API, so it can't be fixed.
+ if (proxyInfo == mGlobalProxy) return;
+ if (proxyInfo != null && proxyInfo.equals(mGlobalProxy)) return;
+ if (mGlobalProxy != null && mGlobalProxy.equals(proxyInfo)) return;
- String host = "";
- int port = 0;
- String exclList = "";
- String pacFileUrl = "";
- if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
- !Uri.EMPTY.equals(proxyProperties.getPacFileUrl()))) {
- if (!proxyProperties.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+ final String host;
+ final int port;
+ final String exclList;
+ final String pacFileUrl;
+ if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
+ !Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
+ if (!proxyInfo.isValid()) {
+ if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
return;
}
- mGlobalProxy = new ProxyInfo(proxyProperties);
+ mGlobalProxy = new ProxyInfo(proxyInfo);
host = mGlobalProxy.getHost();
port = mGlobalProxy.getPort();
exclList = mGlobalProxy.getExclusionListAsString();
- if (!Uri.EMPTY.equals(proxyProperties.getPacFileUrl())) {
- pacFileUrl = proxyProperties.getPacFileUrl().toString();
- }
+ pacFileUrl = Uri.EMPTY.equals(proxyInfo.getPacFileUrl())
+ ? "" : proxyInfo.getPacFileUrl().toString();
} else {
+ host = "";
+ port = 0;
+ exclList = "";
+ pacFileUrl = "";
mGlobalProxy = null;
}
final ContentResolver res = mContext.getContentResolver();
@@ -175,10 +212,45 @@
Binder.restoreCallingIdentity(token);
}
- if (mGlobalProxy == null) {
- proxyProperties = mDefaultProxy;
+ sendProxyBroadcast(mGlobalProxy == null ? mDefaultProxy : proxyInfo);
+ }
+ }
+
+ /**
+ * Sets the default proxy for the device.
+ *
+ * The default proxy is the proxy used for networks that do not have a specific proxy.
+ * @param proxyInfo the proxy spec, or null for no proxy.
+ */
+ public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
+ synchronized (mProxyLock) {
+ if (mDefaultProxy != null && mDefaultProxy.equals(proxyInfo)) {
+ return;
}
- sendProxyBroadcast(proxyProperties);
+ if (mDefaultProxy == proxyInfo) return; // catches repeated nulls
+ if (proxyInfo != null && !proxyInfo.isValid()) {
+ if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+ return;
+ }
+
+ // This call could be coming from the PacManager, containing the port of the local
+ // proxy. If this new proxy matches the global proxy then copy this proxy to the
+ // global (to get the correct local port), and send a broadcast.
+ // TODO: Switch PacManager to have its own message to send back rather than
+ // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
+ if ((mGlobalProxy != null) && (proxyInfo != null)
+ && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
+ && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
+ mGlobalProxy = proxyInfo;
+ sendProxyBroadcast(mGlobalProxy);
+ return;
+ }
+ mDefaultProxy = proxyInfo;
+
+ if (mGlobalProxy != null) return;
+ if (!mDefaultProxyDisabled) {
+ sendProxyBroadcast(proxyInfo);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index df6a6f8..5105941 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1350,8 +1350,11 @@
// do not currently know how to watch for changes in DUN settings.
maybeUpdateConfiguration();
- final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
- mConfig.preferredUpstreamIfaceTypes);
+ final TetheringConfiguration config = mConfig;
+ final NetworkState ns = (config.chooseUpstreamAutomatically)
+ ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
+ : mUpstreamNetworkMonitor.selectPreferredUpstreamType(
+ config.preferredUpstreamIfaceTypes);
if (ns == null) {
if (tryCell) {
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
@@ -1380,9 +1383,7 @@
}
notifyDownstreamsOfNewUpstreamIface(ifaces);
if (ns != null && pertainsToCurrentUpstream(ns)) {
- // If we already have NetworkState for this network examine
- // it immediately, because there likely will be no second
- // EVENT_ON_AVAILABLE (it was already received).
+ // If we already have NetworkState for this network update it immediately.
handleNewUpstreamNetworkState(ns);
} else if (mCurrentUpstreamIfaceSet == null) {
// There are no available upstream networks.
@@ -1498,15 +1499,6 @@
}
switch (arg1) {
- case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
- // The default network changed, or DUN connected
- // before this callback was processed. Updates
- // for the current NetworkCapabilities and
- // LinkProperties have been requested (default
- // request) or are being sent shortly (DUN). Do
- // nothing until they arrive; if no updates
- // arrive there's nothing to do.
- break;
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
handleNewUpstreamNetworkState(ns);
break;
@@ -1539,7 +1531,7 @@
}
mSimChange.startListening();
- mUpstreamNetworkMonitor.start();
+ mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
// TODO: De-duplicate with updateUpstreamWanted() below.
if (upstreamWanted()) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 454c579..dd9fe05 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -27,6 +27,7 @@
import static com.android.internal.R.array.config_tether_usb_regexs;
import static com.android.internal.R.array.config_tether_upstream_types;
import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
import android.content.Context;
@@ -86,6 +87,7 @@
public final String[] tetherableBluetoothRegexs;
public final int dunCheck;
public final boolean isDunRequired;
+ public final boolean chooseUpstreamAutomatically;
public final Collection<Integer> preferredUpstreamIfaceTypes;
public final String[] dhcpRanges;
public final String[] defaultIPv4DNS;
@@ -106,6 +108,7 @@
dunCheck = checkDunRequired(ctx);
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
+ chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
@@ -142,6 +145,8 @@
pw.print("isDunRequired: ");
pw.println(isDunRequired);
+ pw.print("chooseUpstreamAutomatically: ");
+ pw.println(chooseUpstreamAutomatically);
dumpStringArray(pw, "preferredUpstreamIfaceTypes",
preferredUpstreamNames(preferredUpstreamIfaceTypes));
@@ -160,6 +165,7 @@
sj.add(String.format("tetherableBluetoothRegexs:%s",
makeString(tetherableBluetoothRegexs)));
sj.add(String.format("isDunRequired:%s", isDunRequired));
+ sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
@@ -286,6 +292,14 @@
}
}
+ private static boolean getResourceBoolean(Context ctx, int resId) {
+ try {
+ return ctx.getResources().getBoolean(resId);
+ } catch (Resources.NotFoundException e404) {
+ return false;
+ }
+ }
+
private static String[] getResourceStringArray(Context ctx, int resId) {
try {
final String[] strArray = ctx.getResources().getStringArray(resId);
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 0ac7a36..605ee9c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.net.INetd;
+import android.net.NetworkRequest;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
@@ -64,4 +65,8 @@
public boolean isTetheringSupported() {
return true;
}
+
+ public NetworkRequest getDefaultNetworkRequest() {
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3413291..f488be7 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -20,6 +20,9 @@
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import android.content.Context;
import android.os.Handler;
@@ -74,14 +77,13 @@
private static final boolean DBG = false;
private static final boolean VDBG = false;
- public static final int EVENT_ON_AVAILABLE = 1;
- public static final int EVENT_ON_CAPABILITIES = 2;
- public static final int EVENT_ON_LINKPROPERTIES = 3;
- public static final int EVENT_ON_LOST = 4;
+ public static final int EVENT_ON_CAPABILITIES = 1;
+ public static final int EVENT_ON_LINKPROPERTIES = 2;
+ public static final int EVENT_ON_LOST = 3;
public static final int NOTIFY_LOCAL_PREFIXES = 10;
private static final int CALLBACK_LISTEN_ALL = 1;
- private static final int CALLBACK_TRACK_DEFAULT = 2;
+ private static final int CALLBACK_DEFAULT_INTERNET = 2;
private static final int CALLBACK_MOBILE_REQUEST = 3;
private final Context mContext;
@@ -117,7 +119,7 @@
mCM = cm;
}
- public void start() {
+ public void start(NetworkRequest defaultNetworkRequest) {
stop();
final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
@@ -125,8 +127,16 @@
mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
- mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
- cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+ if (defaultNetworkRequest != null) {
+ // This is not really a "request", just a way of tracking the system default network.
+ // It's guaranteed not to actually bring up any networks because it's the same request
+ // as the ConnectivityService default request, and thus shares fate with it. We can't
+ // use registerDefaultNetworkCallback because it will not track the system default
+ // network if there is a VPN that applies to our UID.
+ final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
+ mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
+ cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
+ }
}
public void stop() {
@@ -225,6 +235,20 @@
return typeStatePair.ns;
}
+ // Returns null if no current upstream available.
+ public NetworkState getCurrentPreferredUpstream() {
+ final NetworkState dfltState = (mDefaultInternetNetwork != null)
+ ? mNetworkMap.get(mDefaultInternetNetwork)
+ : null;
+ if (!mDunRequired) return dfltState;
+
+ if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
+
+ // Find a DUN network. Note that code in Tethering causes a DUN request
+ // to be filed, but this might be moved into this class in future.
+ return findFirstDunNetwork(mNetworkMap.values());
+ }
+
public void setCurrentUpstream(Network upstream) {
mTetheringUpstreamNetwork = upstream;
}
@@ -233,72 +257,16 @@
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
- private void handleAvailable(int callbackType, Network network) {
- if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
+ private void handleAvailable(Network network) {
+ if (mNetworkMap.containsKey(network)) return;
- if (!mNetworkMap.containsKey(network)) {
- mNetworkMap.put(network,
- new NetworkState(null, null, null, network, null, null));
- }
-
- // Always request whatever extra information we can, in case this
- // was already up when start() was called, in which case we would
- // not have been notified of any information that had not changed.
- switch (callbackType) {
- case CALLBACK_LISTEN_ALL:
- break;
-
- case CALLBACK_TRACK_DEFAULT:
- if (mDefaultNetworkCallback == null) {
- // The callback was unregistered in the interval between
- // ConnectivityService enqueueing onAvailable() and our
- // handling of it here on the mHandler thread.
- //
- // Clean-up of this network entry is deferred to the
- // handling of onLost() by other callbacks.
- //
- // These request*() calls can be deleted post oag/339444.
- return;
- }
- mDefaultInternetNetwork = network;
- break;
-
- case CALLBACK_MOBILE_REQUEST:
- if (mMobileNetworkCallback == null) {
- // The callback was unregistered in the interval between
- // ConnectivityService enqueueing onAvailable() and our
- // handling of it here on the mHandler thread.
- //
- // Clean-up of this network entry is deferred to the
- // handling of onLost() by other callbacks.
- return;
- }
- break;
- }
-
- // Requesting updates for mListenAllCallback is not currently possible
- // because it's a "listen". Two possible solutions to getting updates
- // about networks without waiting for a change (which might never come)
- // are:
- //
- // [1] extend request{NetworkCapabilities,LinkProperties}() to
- // take a Network argument and have ConnectivityService do
- // what's required (if the network satisfies the request)
- //
- // [2] explicitly file a NetworkRequest for each connectivity type
- // listed as a preferred upstream and wait for these callbacks
- // to be notified (requires tracking many more callbacks).
- //
- // Until this is addressed, networks that exist prior to the "listen"
- // registration and which do not subsequently change will not cause
- // us to learn their NetworkCapabilities nor their LinkProperties.
-
- // TODO: If sufficient information is available to select a more
- // preferable upstream, do so now and notify the target.
- notifyTarget(EVENT_ON_AVAILABLE, network);
+ if (VDBG) Log.d(TAG, "onAvailable for " + network);
+ mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
}
- private void handleNetCap(Network network, NetworkCapabilities newNc) {
+ private void handleNetCap(int callbackType, Network network, NetworkCapabilities newNc) {
+ if (callbackType == CALLBACK_DEFAULT_INTERNET) mDefaultInternetNetwork = network;
+
final NetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
@@ -360,13 +328,17 @@
}
private void handleLost(int callbackType, Network network) {
- if (callbackType == CALLBACK_TRACK_DEFAULT) {
+ if (network.equals(mDefaultInternetNetwork)) {
mDefaultInternetNetwork = null;
- // Receiving onLost() for a default network does not necessarily
- // mean the network is gone. We wait for a separate notification
- // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
- // clearing all state.
- return;
+ // There are few TODOs within ConnectivityService's rematching code
+ // pertaining to spurious onLost() notifications.
+ //
+ // TODO: simplify this, probably if favor of code that:
+ // - selects a new upstream if mTetheringUpstreamNetwork has
+ // been lost (by any callback)
+ // - deletes the entry from the map only when the LISTEN_ALL
+ // callback gets notified.
+ if (callbackType == CALLBACK_DEFAULT_INTERNET) return;
}
if (!mNetworkMap.containsKey(network)) {
@@ -416,17 +388,19 @@
@Override
public void onAvailable(Network network) {
- handleAvailable(mCallbackType, network);
+ handleAvailable(network);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- handleNetCap(network, newNc);
+ handleNetCap(mCallbackType, network, newNc);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
handleLinkProp(network, newLp);
+ // TODO(b/110335330): reduce the number of times this is called by
+ // only recomputing on the LISTEN_ALL callback.
recomputeLocalPrefixes();
}
@@ -443,6 +417,8 @@
@Override
public void onLost(Network network) {
handleLost(mCallbackType, network);
+ // TODO(b/110335330): reduce the number of times this is called by
+ // only recomputing on the LISTEN_ALL callback.
recomputeLocalPrefixes();
}
}
@@ -509,4 +485,31 @@
if (nc == null || !nc.hasSignalStrength()) return "unknown";
return Integer.toString(nc.getSignalStrength());
}
+
+ private static boolean isCellular(NetworkState ns) {
+ return (ns != null) && isCellular(ns.networkCapabilities);
+ }
+
+ private static boolean isCellular(NetworkCapabilities nc) {
+ return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) &&
+ nc.hasCapability(NET_CAPABILITY_NOT_VPN);
+ }
+
+ private static boolean hasCapability(NetworkState ns, int netCap) {
+ return (ns != null) && (ns.networkCapabilities != null) &&
+ ns.networkCapabilities.hasCapability(netCap);
+ }
+
+ private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
+ return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) &&
+ !isCellular(ns.networkCapabilities);
+ }
+
+ private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
+ for (NetworkState ns : netStates) {
+ if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
+ }
+
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 11f0701..811dc75 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -30,10 +30,6 @@
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteQueryBuilder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -510,10 +506,6 @@
readAccountInfoLocked();
readStatusLocked();
readStatisticsLocked();
- readAndDeleteLegacyAccountInfoLocked();
- writeAccountInfoLocked();
- writeStatusLocked();
- writeStatisticsLocked();
if (mLogger.enabled()) {
final int size = mAuthorities.size();
@@ -1581,7 +1573,6 @@
readAccountInfoLocked();
readStatusLocked();
readStatisticsLocked();
- readAndDeleteLegacyAccountInfoLocked();
writeAccountInfoLocked();
writeStatusLocked();
writeStatisticsLocked();
@@ -1998,146 +1989,6 @@
}
}
- static int getIntColumn(Cursor c, String name) {
- return c.getInt(c.getColumnIndex(name));
- }
-
- static long getLongColumn(Cursor c, String name) {
- return c.getLong(c.getColumnIndex(name));
- }
-
- /**
- * TODO Remove it. It's super old code that was used to migrate the information from a sqlite
- * database that we used a long time ago, and is no longer relevant.
- */
- private void readAndDeleteLegacyAccountInfoLocked() {
- // Look for old database to initialize from.
- File file = mContext.getDatabasePath("syncmanager.db");
- if (!file.exists()) {
- return;
- }
- String path = file.getPath();
- SQLiteDatabase db = null;
- try {
- db = SQLiteDatabase.openDatabase(path, null,
- SQLiteDatabase.OPEN_READONLY);
- } catch (SQLiteException e) {
- }
-
- if (db != null) {
- final boolean hasType = db.getVersion() >= 11;
-
- // Copy in all of the status information, as well as accounts.
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Slog.v(TAG_FILE, "Reading legacy sync accounts db");
- }
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables("stats, status");
- HashMap<String,String> map = new HashMap<String,String>();
- map.put("_id", "status._id as _id");
- map.put("account", "stats.account as account");
- if (hasType) {
- map.put("account_type", "stats.account_type as account_type");
- }
- map.put("authority", "stats.authority as authority");
- map.put("totalElapsedTime", "totalElapsedTime");
- map.put("numSyncs", "numSyncs");
- map.put("numSourceLocal", "numSourceLocal");
- map.put("numSourcePoll", "numSourcePoll");
- map.put("numSourceServer", "numSourceServer");
- map.put("numSourceUser", "numSourceUser");
- map.put("lastSuccessSource", "lastSuccessSource");
- map.put("lastSuccessTime", "lastSuccessTime");
- map.put("lastFailureSource", "lastFailureSource");
- map.put("lastFailureTime", "lastFailureTime");
- map.put("lastFailureMesg", "lastFailureMesg");
- map.put("pending", "pending");
- qb.setProjectionMap(map);
- qb.appendWhere("stats._id = status.stats_id");
- Cursor c = qb.query(db, null, null, null, null, null, null);
- while (c.moveToNext()) {
- String accountName = c.getString(c.getColumnIndex("account"));
- String accountType = hasType
- ? c.getString(c.getColumnIndex("account_type")) : null;
- if (accountType == null) {
- accountType = "com.google";
- }
- String authorityName = c.getString(c.getColumnIndex("authority"));
- AuthorityInfo authority =
- this.getOrCreateAuthorityLocked(
- new EndPoint(new Account(accountName, accountType),
- authorityName,
- 0 /* legacy is single-user */)
- , -1,
- false);
- if (authority != null) {
- int i = mSyncStatus.size();
- boolean found = false;
- SyncStatusInfo st = null;
- while (i > 0) {
- i--;
- st = mSyncStatus.valueAt(i);
- if (st.authorityId == authority.ident) {
- found = true;
- break;
- }
- }
- if (!found) {
- st = new SyncStatusInfo(authority.ident);
- mSyncStatus.put(authority.ident, st);
- }
- st.totalStats.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
- st.totalStats.numSyncs = getIntColumn(c, "numSyncs");
- st.totalStats.numSourceLocal = getIntColumn(c, "numSourceLocal");
- st.totalStats.numSourcePoll = getIntColumn(c, "numSourcePoll");
- st.totalStats.numSourceOther = getIntColumn(c, "numSourceServer");
- st.totalStats.numSourceUser = getIntColumn(c, "numSourceUser");
- st.totalStats.numSourcePeriodic = 0;
- st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
- st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
- st.lastFailureSource = getIntColumn(c, "lastFailureSource");
- st.lastFailureTime = getLongColumn(c, "lastFailureTime");
- st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
- st.pending = getIntColumn(c, "pending") != 0;
- }
- }
-
- c.close();
-
- // Retrieve the settings.
- qb = new SQLiteQueryBuilder();
- qb.setTables("settings");
- c = qb.query(db, null, null, null, null, null, null);
- while (c.moveToNext()) {
- String name = c.getString(c.getColumnIndex("name"));
- String value = c.getString(c.getColumnIndex("value"));
- if (name == null) continue;
- if (name.equals("listen_for_tickles")) {
- setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0,
- ContentResolver.SYNC_EXEMPTION_NONE, SyncLogger.CALLING_UID_SELF);
- } else if (name.startsWith("sync_provider_")) {
- String provider = name.substring("sync_provider_".length(),
- name.length());
- int i = mAuthorities.size();
- while (i > 0) {
- i--;
- AuthorityInfo authority = mAuthorities.valueAt(i);
- if (authority.target.provider.equals(provider)) {
- authority.enabled = value == null || Boolean.parseBoolean(value);
- authority.syncable = 1;
- }
- }
- }
- }
-
- c.close();
-
- db.close();
-
- (new File(path)).delete();
- }
- }
-
public static final int STATUS_FILE_END = 0;
public static final int STATUS_FILE_ITEM = 100;
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
deleted file mode 100644
index c9efcf2..0000000
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Arrays;
-
-/**
- * A class to keep track of the enrollment state for a given client.
- */
-public abstract class EnrollClient extends ClientMonitor {
- private static final long MS_PER_SEC = 1000;
- private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
- private byte[] mCryptoToken;
-
- public EnrollClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
- boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
- }
-
- @Override
- public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
- if (groupId != getGroupId()) {
- Slog.w(TAG, "groupId != getGroupId(), groupId: " + groupId +
- " getGroupId():" + getGroupId());
- }
- if (remaining == 0) {
- FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
- getTargetUserId());
- }
- return sendEnrollResult(fingerId, groupId, remaining);
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
- IFingerprintServiceReceiver receiver = getReceiver();
- if (receiver == null)
- return true; // client not listening
-
- vibrateSuccess();
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
- try {
- receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
- return remaining == 0;
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify EnrollResult:", e);
- return true;
- }
- }
-
- @Override
- public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "enroll: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
- try {
- final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
- if (result != 0) {
- Slog.w(TAG, "startEnroll failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingerprintd_enroll_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startEnroll failed", e);
- }
- return 0; // success
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopEnroll: already cancelled!");
- return 0;
- }
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopEnrollment: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- try {
- final int result = daemon.cancel();
- if (result != 0) {
- Slog.w(TAG, "startEnrollCancel failed, result = " + result);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "stopEnrollment failed", e);
- }
- if (initiatedByClient) {
- onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
- }
- mAlreadyCancelled = true;
- return 0;
- }
-
- @Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
- return true; // Invalid for EnrollClient
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for enroll!");
- return true; // Invalid for EnrollClient
- }
-
- @Override
- public boolean onAuthenticated(int fingerId, int groupId) {
- if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
- return true; // Invalid for EnrollClient
- }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
deleted file mode 100644
index b6bbd1b..0000000
--- a/services/core/java/com/android/server/fingerprint/EnumerateClient.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-
-/**
- * A class to keep track of the enumeration state for a given client.
- */
-public abstract class EnumerateClient extends ClientMonitor {
- public EnumerateClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int groupId, int userId,
- boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- }
-
- @Override
- public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- // The fingerprint template ids will be removed when we get confirmation from the HAL
- try {
- final int result = daemon.enumerate();
- if (result != 0) {
- Slog.w(TAG, "start enumerate for user " + getTargetUserId()
- + " failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingerprintd_enum_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startEnumeration failed", e);
- }
- return 0;
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopEnumerate: already cancelled!");
- return 0;
- }
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- try {
- final int result = daemon.cancel();
- if (result != 0) {
- Slog.w(TAG, "stop enumeration failed, result=" + result);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "stopEnumeration failed", e);
- return ERROR_ESRCH;
- }
-
- // We don't actually stop enumerate, but inform the client that the cancel operation
- // succeeded so we can start the next operation.
- if (initiatedByClient) {
- onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
- }
- mAlreadyCancelled = true;
- return 0; // success
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- IFingerprintServiceReceiver receiver = getReceiver();
- if (receiver == null)
- return true; // client not listening
- try {
- receiver.onEnumerated(getHalDeviceId(), fingerId, groupId, remaining);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify enumerated:", e);
- }
- return remaining == 0;
- }
-
- @Override
- public boolean onAuthenticated(int fingerId, int groupId) {
- if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enumerate!");
- return true; // Invalid for Enumerate.
- }
-
- @Override
- public boolean onEnrollResult(int fingerId, int groupId, int rem) {
- if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
- return true; // Invalid for Enumerate.
- }
-
- @Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
- return true; // Invalid for Enumerate.
- }
-}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
deleted file mode 100644
index 8e77820..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ /dev/null
@@ -1,1595 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.fingerprint;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
-import static android.Manifest.permission.USE_BIOMETRIC;
-import static android.Manifest.permission.USE_FINGERPRINT;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
-import android.app.PendingIntent;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintClientActiveCallback;
-import android.hardware.fingerprint.IFingerprintService;
-import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.IRemoteCallback;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.security.KeyStore;
-import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.SystemService;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * A service to manage multiple clients that want to access the fingerprint HAL API.
- * The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint-related events.
- *
- * @hide
- */
-public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient {
- static final String TAG = "FingerprintService";
- static final boolean DEBUG = true;
- private static final boolean CLEANUP_UNUSED_FP = true;
- private static final String FP_DATA_DIR = "fpdata";
- private static final int MSG_USER_SWITCHING = 10;
- private static final String ACTION_LOCKOUT_RESET =
- "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
- private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
-
- private class PerformanceStats {
- int accept; // number of accepted fingerprints
- int reject; // number of rejected fingerprints
- int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
- // acquisition in some cases (too fast, too slow, dirty sensor, etc.)
- int lockout; // total number of lockouts
- int permanentLockout; // total number of permanent lockouts
- }
-
- private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
- new ArrayList<>();
- private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
- new CopyOnWriteArrayList<>();
- private final Map<Integer, Long> mAuthenticatorIds =
- Collections.synchronizedMap(new HashMap<>());
- private final AppOpsManager mAppOps;
- private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
- private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
- private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
-
- private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
- private final String mKeyguardPackage;
- private int mCurrentUserId = UserHandle.USER_NULL;
- private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
- private Context mContext;
- private long mHalDeviceId;
- private SparseBooleanArray mTimedLockoutCleared;
- private SparseIntArray mFailedAttempts;
- @GuardedBy("this")
- private IBiometricsFingerprint mDaemon;
- private IStatusBarService mStatusBarService;
- private final IActivityManager mActivityManager;
- private final IActivityTaskManager mActivityTaskManager;
- private final PowerManager mPowerManager;
- private final AlarmManager mAlarmManager;
- private final UserManager mUserManager;
- private ClientMonitor mCurrentClient;
- private ClientMonitor mPendingClient;
- private PerformanceStats mPerformanceStats;
-
- private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
- private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
-
- private class UserFingerprint {
- Fingerprint f;
- int userId;
- public UserFingerprint(Fingerprint f, int userId) {
- this.f = f;
- this.userId = userId;
- }
- }
-
- // Normal fingerprint authentications are tracked by mPerformanceMap.
- private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
-
- // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
- private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1);
- break;
-
- default:
- Slog.w(TAG, "Unknown message:" + msg.what);
- }
- }
- };
-
- private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
- final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
- resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
- }
- }
- };
-
- private final Runnable mResetFailedAttemptsForCurrentUserRunnable = new Runnable() {
- @Override
- public void run() {
- resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
- };
-
- private final Runnable mResetClientState = new Runnable() {
- @Override
- public void run() {
- // Warning: if we get here, the driver never confirmed our call to cancel the current
- // operation (authenticate, enroll, remove, enumerate, etc), which is
- // really bad. The result will be a 3-second delay in starting each new client.
- // If you see this on a device, make certain the driver notifies with
- // {@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} in response to cancel()
- // once it has successfully switched to the IDLE state in the fingerprint HAL.
- // Additionally,{@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} should only be sent
- // in response to an actual cancel() call.
- Slog.w(TAG, "Client "
- + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
- + " failed to respond to cancel, starting client "
- + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
-
- mCurrentClient = null;
- startClient(mPendingClient, false);
- }
- };
-
- private final TaskStackListener mTaskStackListener = new TaskStackListener() {
- @Override
- public void onTaskStackChanged() {
- try {
- if (!(mCurrentClient instanceof AuthenticationClient)) {
- return;
- }
- final String currentClient = mCurrentClient.getOwnerString();
- if (isKeyguard(currentClient)) {
- return; // Keyguard is always allowed
- }
- List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(currentClient)) {
- Slog.e(TAG, "Stopping background authentication, top: " + topPackage
- + " currentClient: " + currentClient);
- mCurrentClient.stop(false /* initiatedByClient */);
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get running tasks", e);
- }
- }
- };
-
- public FingerprintService(Context context) {
- super(context);
- mContext = context;
- mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
- com.android.internal.R.string.config_keyguardComponent)).getPackageName();
- mAppOps = context.getSystemService(AppOpsManager.class);
- mPowerManager = mContext.getSystemService(PowerManager.class);
- mAlarmManager = mContext.getSystemService(AlarmManager.class);
- mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
- RESET_FINGERPRINT_LOCKOUT, null /* handler */);
- mUserManager = UserManager.get(mContext);
- mTimedLockoutCleared = new SparseBooleanArray();
- mFailedAttempts = new SparseIntArray();
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mActivityManager = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
- .getService();
- mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
- Context.ACTIVITY_TASK_SERVICE)).getService();
- }
-
- @Override
- public void serviceDied(long cookie) {
- Slog.v(TAG, "fingerprint HAL died");
- MetricsLogger.count(mContext, "fingerprintd_died", 1);
- handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /*vendorCode */);
- }
-
- public synchronized IBiometricsFingerprint getFingerprintDaemon() {
- if (mDaemon == null) {
- Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
- try {
- mDaemon = IBiometricsFingerprint.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened. Logged below.
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get biometric interface", e);
- }
- if (mDaemon == null) {
- Slog.w(TAG, "fingerprint HIDL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0);
-
- try {
- mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to open fingerprint HAL", e);
- mDaemon = null; // try again later!
- }
-
- if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
- if (mHalDeviceId != 0) {
- loadAuthenticatorIds();
- updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
- } else {
- Slog.w(TAG, "Failed to open Fingerprint HAL!");
- MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
- mDaemon = null;
- }
- }
- return mDaemon;
- }
-
- /** Populates existing authenticator ids. To be used only during the start of the service. */
- private void loadAuthenticatorIds() {
- // This operation can be expensive, so keep track of the elapsed time. Might need to move to
- // background if it takes too long.
- long t = System.currentTimeMillis();
- mAuthenticatorIds.clear();
- for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
- int userId = getUserOrWorkProfileId(null, user.id);
- if (!mAuthenticatorIds.containsKey(userId)) {
- updateActiveGroup(userId, null);
- }
- }
-
- t = System.currentTimeMillis() - t;
- if (t > 1000) {
- Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
- }
- }
-
- /**
- * This method should be called upon connection to the daemon, and when user switches.
- * @param userId
- */
- private void doFingerprintCleanupForUser(int userId) {
- if (CLEANUP_UNUSED_FP) {
- enumerateUser(userId);
- }
- }
-
- private void clearEnumerateState() {
- if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
- mUnknownFingerprints.clear();
- }
-
- private void enumerateUser(int userId) {
- if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- startEnumerate(mToken, userId, null, restricted, true /* internal */);
- }
-
- // Remove unknown fingerprints from hardware
- private void cleanupUnknownFingerprints() {
- if (!mUnknownFingerprints.isEmpty()) {
- UserFingerprint uf = mUnknownFingerprints.get(0);
- mUnknownFingerprints.remove(uf);
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
- restricted, true /* internal */);
- } else {
- clearEnumerateState();
- }
- }
-
- protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- ClientMonitor client = mCurrentClient;
-
- if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
- return;
- }
- client.onEnumerationResult(fingerId, groupId, remaining);
-
- // All fingerprints in hardware for this user were enumerated
- if (remaining == 0) {
- if (client instanceof InternalEnumerateClient) {
- List<Fingerprint> unknownFingerprints =
- ((InternalEnumerateClient) client).getUnknownFingerprints();
-
- if (!unknownFingerprints.isEmpty()) {
- Slog.w(TAG, "Adding " + unknownFingerprints.size() +
- " fingerprints for deletion");
- }
- for (Fingerprint f : unknownFingerprints) {
- mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
- }
- removeClient(client);
- cleanupUnknownFingerprints();
- } else {
- removeClient(client);
- }
- }
- }
-
- protected void handleError(long deviceId, int error, int vendorCode) {
- ClientMonitor client = mCurrentClient;
- if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) {
- clearEnumerateState();
- }
- if (client != null && client.onError(error, vendorCode)) {
- removeClient(client);
- }
-
- if (DEBUG) Slog.v(TAG, "handleError(client="
- + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
- // This is the magic code that starts the next client when the old client finishes.
- if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
- mHandler.removeCallbacks(mResetClientState);
- if (mPendingClient != null) {
- if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
- startClient(mPendingClient, false);
- mPendingClient = null;
- }
- } else if (error == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
- // If we get HW_UNAVAILABLE, try to connect again later...
- Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
- synchronized (this) {
- mDaemon = null;
- mHalDeviceId = 0;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
- }
-
- protected void handleRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "Removed: fid=" + fingerId
- + ", gid=" + groupId
- + ", dev=" + deviceId
- + ", rem=" + remaining);
-
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
- removeClient(client);
- // When the last fingerprint of a group is removed, update the authenticator id
- if (!hasEnrolledFingerprints(groupId)) {
- updateActiveGroup(groupId, null);
- }
- }
- if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
- cleanupUnknownFingerprints();
- } else if (client instanceof InternalRemovalClient){
- clearEnumerateState();
- }
- }
-
- protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
- ArrayList<Byte> token) {
- ClientMonitor client = mCurrentClient;
- if (fingerId != 0) {
- // Ugh...
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
- }
- // Send to Keystore
- KeyStore.getInstance().addAuthToken(byteToken);
- }
- if (client != null && client.onAuthenticated(fingerId, groupId)) {
- removeClient(client);
- }
- if (fingerId != 0) {
- mPerformanceStats.accept++;
- } else {
- mPerformanceStats.reject++;
- }
- }
-
- protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
- removeClient(client);
- }
- if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
- && client instanceof AuthenticationClient) {
- // ignore enrollment acquisitions or acquisitions when we're locked out
- mPerformanceStats.acquire++;
- }
- }
-
- protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
- removeClient(client);
- // When enrollment finishes, update this group's authenticator id, as the HAL has
- // already generated a new authenticator id when the new fingerprint is enrolled.
- updateActiveGroup(groupId, null);
- }
- }
-
- private void userActivity() {
- long now = SystemClock.uptimeMillis();
- mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
- }
-
- void handleUserSwitching(int userId) {
- if (mCurrentClient instanceof InternalRemovalClient
- || mCurrentClient instanceof InternalEnumerateClient) {
- Slog.w(TAG, "User switched while performing cleanup");
- removeClient(mCurrentClient);
- clearEnumerateState();
- }
- updateActiveGroup(userId, null);
- doFingerprintCleanupForUser(userId);
- }
-
- private void removeClient(ClientMonitor client) {
- if (client != null) {
- client.destroy();
- if (client != mCurrentClient && mCurrentClient != null) {
- Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
- + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
- }
- }
- if (mCurrentClient != null) {
- if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
- mCurrentClient = null;
- }
- if (mPendingClient == null) {
- notifyClientActiveCallbacks(false);
- }
- }
-
- private int getLockoutMode() {
- final int currentUser = ActivityManager.getCurrentUser();
- final int failedAttempts = mFailedAttempts.get(currentUser, 0);
- if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
- return AuthenticationClient.LOCKOUT_PERMANENT;
- } else if (failedAttempts > 0 &&
- mTimedLockoutCleared.get(currentUser, false) == false
- && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
- return AuthenticationClient.LOCKOUT_TIMED;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
-
- private void scheduleLockoutResetForUser(int userId) {
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
- getLockoutResetIntentForUser(userId));
- }
-
- private void cancelLockoutResetForUser(int userId) {
- mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
- }
-
- private PendingIntent getLockoutResetIntentForUser(int userId) {
- return PendingIntent.getBroadcast(mContext, userId,
- new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- public long startPreEnroll(IBinder token) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
- return 0;
- }
- try {
- return daemon.preEnroll();
- } catch (RemoteException e) {
- Slog.e(TAG, "startPreEnroll failed", e);
- }
- return 0;
- }
-
- public int startPostEnroll(IBinder token) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
- return 0;
- }
- try {
- return daemon.postEnroll();
- } catch (RemoteException e) {
- Slog.e(TAG, "startPostEnroll failed", e);
- }
- return 0;
- }
-
- /**
- * Calls fingerprint HAL to switch states to the new task. If there's already a current task,
- * it calls cancel() and sets mPendingClient to begin when the current task finishes
- * ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
- * @param newClient the new client that wants to connect
- * @param initiatedByClient true for authenticate, remove and enroll
- */
- private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
- ClientMonitor currentClient = mCurrentClient;
- if (currentClient != null) {
- if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
- if (currentClient instanceof InternalEnumerateClient ||
- currentClient instanceof InternalRemovalClient) {
- // This condition means we're currently running internal diagnostics to
- // remove extra fingerprints in the hardware and/or the software
- // TODO: design an escape hatch in case client never finishes
- if (newClient != null) {
- Slog.w(TAG, "Internal cleanup in progress but trying to start client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- }
- }
- else {
- currentClient.stop(initiatedByClient);
- }
- mPendingClient = newClient;
- mHandler.removeCallbacks(mResetClientState);
- mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
- } else if (newClient != null) {
- mCurrentClient = newClient;
- if (DEBUG) Slog.v(TAG, "starting client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- notifyClientActiveCallbacks(true);
-
- newClient.start();
- }
- }
-
- void startRemove(IBinder token, int fingerId, int groupId, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
- if (token == null) {
- Slog.w(TAG, "startRemove: token is null");
- return;
- }
- if (receiver == null) {
- Slog.w(TAG, "startRemove: receiver is null");
- return;
- }
-
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startRemove: no fingerprint HAL!");
- return;
- }
-
- if (internal) {
- Context context = getContext();
- InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
- token, receiver, fingerId, groupId, userId, restricted,
- context.getOpPackageName()) {
- @Override
- public void notifyUserActivity() {
-
- }
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- else {
- RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
- receiver, fingerId, groupId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- }
-
- void startEnumerate(IBinder token, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
- return;
- }
- if (internal) {
- List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
- Context context = getContext();
- InternalEnumerateClient client = new InternalEnumerateClient(context, mHalDeviceId,
- token, receiver, userId, userId, restricted, context.getOpPackageName(),
- enrolledList) {
- @Override
- public void notifyUserActivity() {
-
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- else {
- EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
- receiver, userId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- }
-
- public List<Fingerprint> getEnrolledFingerprints(int userId) {
- return mFingerprintUtils.getFingerprintsForUser(mContext, userId);
- }
-
- public boolean hasEnrolledFingerprints(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
- }
- return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
- }
-
- boolean hasPermission(String permission) {
- return getContext().checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- void checkPermission(String permission) {
- getContext().enforceCallingOrSelfPermission(permission,
- "Must have " + permission + " permission.");
- }
-
- int getEffectiveUserId(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um != null) {
- final long callingIdentity = Binder.clearCallingIdentity();
- userId = um.getCredentialOwnerProfile(userId);
- Binder.restoreCallingIdentity(callingIdentity);
- } else {
- Slog.e(TAG, "Unable to acquire UserManager");
- }
- return userId;
- }
-
- boolean isCurrentUserOrProfile(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um == null) {
- Slog.e(TAG, "Unable to acquire UserManager");
- return false;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- // Allow current user or profiles of the current user...
- for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
- if (profileId == userId) {
- return true;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- return false;
- }
-
- private boolean isForegroundActivity(int uid, int pid) {
- try {
- List<RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- int N = procs.size();
- for (int i = 0; i < N; i++) {
- RunningAppProcessInfo proc = procs.get(i);
- if (proc.pid == pid && proc.uid == uid
- && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
- return true;
- }
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "am.getRunningAppProcesses() failed");
- }
- return false;
- }
-
- /**
- * @param opPackageName name of package for caller
- * @param requireForeground only allow this call while app is in the foreground
- * @return true if caller can use fingerprint API
- */
- private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
- int pid, int userId) {
- if (getContext().checkCallingPermission(USE_FINGERPRINT)
- != PackageManager.PERMISSION_GRANTED) {
- checkPermission(USE_BIOMETRIC);
- }
-
- if (isKeyguard(opPackageName)) {
- return true; // Keyguard is always allowed
- }
- if (!isCurrentUserOrProfile(userId)) {
- Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
- return false;
- }
- if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
- return false;
- }
- if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
- Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
- return false;
- }
- return true;
- }
-
- /**
- * @param opPackageName package of the caller
- * @return true if this is the same client currently using fingerprint
- */
- private boolean currentClient(String opPackageName) {
- return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
- }
-
- /**
- * @param clientPackage
- * @return true if this is keyguard package
- */
- private boolean isKeyguard(String clientPackage) {
- return mKeyguardPackage.equals(clientPackage);
- }
-
- private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
- if (!mLockoutMonitors.contains(monitor)) {
- mLockoutMonitors.add(monitor);
- }
- }
-
- private void removeLockoutResetCallback(
- FingerprintServiceLockoutResetMonitor monitor) {
- mLockoutMonitors.remove(monitor);
- }
-
- private void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
- private void notifyClientActiveCallbacks(boolean isActive) {
- List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
- for (int i = 0; i < callbacks.size(); i++) {
- try {
- callbacks.get(i).onClientActiveChanged(isActive);
- } catch (RemoteException re) {
- // If the remote is dead, stop notifying it
- mClientActiveCallbacks.remove(callbacks.get(i));
- }
- }
- }
-
- private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
- IFingerprintServiceReceiver receiver, int flags, boolean restricted,
- String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver) {
- updateActiveGroup(groupId, opPackageName);
-
- if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
-
- AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
- receiver, mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService) {
- @Override
- public void onStart() {
- try {
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not register task stack listener", e);
- }
- }
-
- @Override
- public void onStop() {
- try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not unregister task stack listener", e);
- }
- }
-
- @Override
- public int handleFailedAttempt() {
- final int currentUser = ActivityManager.getCurrentUser();
- mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
- mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
- final int lockoutMode = getLockoutMode();
- if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
- mPerformanceStats.permanentLockout++;
- } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
- mPerformanceStats.lockout++;
- }
-
- // Failing multiple times will continue to push out the lockout time
- if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- scheduleLockoutResetForUser(currentUser);
- return lockoutMode;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
-
- @Override
- public void resetFailedAttempts() {
- FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
-
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
-
- int lockoutMode = getLockoutMode();
- if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(TAG, "In lockout mode(" + lockoutMode +
- ") ; disallowing authentication");
- int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
- if (!client.onError(errorCode, 0 /* vendorCode */)) {
- Slog.w(TAG, "Cannot send permanent lockout message to client");
- }
- return;
- }
- startClient(client, true /* initiatedByClient */);
- }
-
- private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
- IFingerprintServiceReceiver receiver, int flags, boolean restricted,
- String opPackageName) {
- updateActiveGroup(userId, opPackageName);
-
- final int groupId = userId; // default group for fingerprint enrollment
-
- EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
- userId, groupId, cryptoToken, restricted, opPackageName) {
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
-
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
- };
- startClient(client, true /* initiatedByClient */);
- }
-
- // attempt counter should only be cleared when Keyguard goes away or when
- // a fingerprint is successfully authenticated
- protected void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
- if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter);
- }
- if (clearAttemptCounter) {
- mFailedAttempts.put(userId, 0);
- }
- mTimedLockoutCleared.put(userId, true);
- // If we're asked to reset failed attempts externally (i.e. from Keyguard),
- // the alarm might still be pending; remove it.
- cancelLockoutResetForUser(userId);
- notifyLockoutResetMonitors();
- }
-
- private class FingerprintServiceLockoutResetMonitor implements IBinder.DeathRecipient {
-
- private static final long WAKELOCK_TIMEOUT_MS = 2000;
- private final IFingerprintServiceLockoutResetCallback mCallback;
- private final WakeLock mWakeLock;
-
- public FingerprintServiceLockoutResetMonitor(
- IFingerprintServiceLockoutResetCallback callback) {
- mCallback = callback;
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "lockout reset callback");
- try {
- mCallback.asBinder().linkToDeath(FingerprintServiceLockoutResetMonitor.this, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "caught remote exception in linkToDeath", e);
- }
- }
-
- public void sendLockoutReset() {
- if (mCallback != null) {
- try {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
- mCallback.onLockoutReset(mHalDeviceId, new IRemoteCallback.Stub() {
-
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- releaseWakelock();
- }
- });
- } catch (DeadObjectException e) {
- Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
- mHandler.post(mRemoveCallbackRunnable);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
- releaseWakelock();
- }
- }
- }
-
- private final Runnable mRemoveCallbackRunnable = new Runnable() {
- @Override
- public void run() {
- releaseWakelock();
- removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
- }
- };
-
- @Override
- public void binderDied() {
- Slog.e(TAG, "Lockout reset callback binder died");
- mHandler.post(mRemoveCallbackRunnable);
- }
-
- private void releaseWakelock() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
-
- private IBiometricsFingerprintClientCallback mDaemonCallback =
- new IBiometricsFingerprintClientCallback.Stub() {
-
- @Override
- public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleEnrollResult(deviceId, fingerId, groupId, remaining);
- }
- });
- }
-
- @Override
- public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleAcquired(deviceId, acquiredInfo, vendorCode);
- }
- });
- }
-
- @Override
- public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
- ArrayList<Byte> token) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleAuthenticated(deviceId, fingerId, groupId, token);
- }
- });
- }
-
- @Override
- public void onError(final long deviceId, final int error, final int vendorCode) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleError(deviceId, error, vendorCode);
- }
- });
- }
-
- @Override
- public void onRemoved(final long deviceId, final int fingerId, final int groupId, final int remaining) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleRemoved(deviceId, fingerId, groupId, remaining);
- }
- });
- }
-
- @Override
- public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleEnumerate(deviceId, fingerId, groupId, remaining);
- }
- });
- }
- };
-
- private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
- @Override // Binder call
- public long preEnroll(IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- return startPreEnroll(token);
- }
-
- @Override // Binder call
- public int postEnroll(IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- return startPostEnroll(token);
- }
-
- @Override // Binder call
- public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
- final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName) {
- checkPermission(MANAGE_FINGERPRINT);
- final int limit = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-
- final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many fingerprints registered");
- return;
- }
-
- // Group ID is arbitrarily set to parent profile user ID. It just represents
- // the default fingerprints for the user.
- if (!isCurrentUserOrProfile(userId)) {
- return;
- }
-
- final boolean restricted = isRestricted();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startEnrollment(token, cryptoToken, userId, receiver, flags,
- restricted, opPackageName);
- }
- });
- }
-
- private boolean isRestricted() {
- // Only give privileged apps (like Settings) access to fingerprint info
- final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- return restricted;
- }
-
- @Override // Binder call
- public void cancelEnrollment(final IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientMonitor client = mCurrentClient;
- if (client instanceof EnrollClient && client.getToken() == token) {
- client.stop(client.getToken() == token);
- }
- }
- });
- }
-
- @Override // Binder call
- public void authenticate(final IBinder token, final long opId, final int groupId,
- final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName, final Bundle bundle,
- final IBiometricPromptReceiver dialogReceiver) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- final boolean restricted = isRestricted();
-
- if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
- return;
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
-
- // Get performance stats object for this user.
- HashMap<Integer, PerformanceStats> pmap
- = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
- PerformanceStats stats = pmap.get(mCurrentUserId);
- if (stats == null) {
- stats = new PerformanceStats();
- pmap.put(mCurrentUserId, stats);
- }
- mPerformanceStats = stats;
-
- startAuthentication(token, opId, callingUserId, groupId, receiver,
- flags, restricted, opPackageName, bundle, dialogReceiver);
- }
- });
- }
-
- @Override // Binder call
- public void cancelAuthentication(final IBinder token, final String opPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
-
- if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
- return;
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientMonitor client = mCurrentClient;
- if (client instanceof AuthenticationClient) {
- if (client.getToken() == token) {
- if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
- client.stop(client.getToken() == token);
- } else {
- if (DEBUG) Slog.v(TAG, "can't stop client "
- + client.getOwnerString() + " since tokens don't match");
- }
- } else if (client != null) {
- if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
- + client.getOwnerString());
- }
- }
- });
- }
-
- @Override // Binder call
- public void setActiveUser(final int userId) {
- checkPermission(MANAGE_FINGERPRINT);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- updateActiveGroup(userId, null);
- }
- });
- }
-
- @Override // Binder call
- public void remove(final IBinder token, final int fingerId, final int groupId,
- final int userId, final IFingerprintServiceReceiver receiver) {
- checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
- final boolean restricted = isRestricted();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startRemove(token, fingerId, groupId, userId, receiver,
- restricted, false /* internal */);
- }
- });
- }
-
- @Override // Binder call
- public void enumerate(final IBinder token, final int userId,
- final IFingerprintServiceReceiver receiver) {
- checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
- final boolean restricted = isRestricted();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startEnumerate(token, userId, receiver, restricted, false /* internal */);
- }
- });
- }
-
- @Override // Binder call
- public boolean isHardwareDetected(long deviceId, String opPackageName) {
- if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return false;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- return daemon != null && mHalDeviceId != 0;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override // Binder call
- public void rename(final int fingerId, final int groupId, final String name) {
- checkPermission(MANAGE_FINGERPRINT);
- if (!isCurrentUserOrProfile(groupId)) {
- return;
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mFingerprintUtils.renameFingerprintForUser(mContext, fingerId,
- groupId, name);
- }
- });
- }
-
- @Override // Binder call
- public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return Collections.emptyList();
- }
-
- return FingerprintService.this.getEnrolledFingerprints(userId);
- }
-
- @Override // Binder call
- public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return false;
- }
-
- return FingerprintService.this.hasEnrolledFingerprints(userId);
- }
-
- @Override // Binder call
- public long getAuthenticatorId(String opPackageName) {
- // In this method, we're not checking whether the caller is permitted to use fingerprint
- // API because current authenticator ID is leaked (in a more contrived way) via Android
- // Keystore (android.security.keystore package): the user of that API can create a key
- // which requires fingerprint authentication for its use, and then query the key's
- // characteristics (hidden API) which returns, among other things, fingerprint
- // authenticator ID which was active at key creation time.
- //
- // Reason: The part of Android Keystore which runs inside an app's process invokes this
- // method in certain cases. Those cases are not always where the developer demonstrates
- // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
- // unexpected SecurityException this method does not check whether its caller is
- // permitted to use fingerprint API.
- //
- // The permission check should be restored once Android Keystore no longer invokes this
- // method from inside app processes.
-
- return FingerprintService.this.getAuthenticatorId(opPackageName);
- }
-
- @Override // Binder call
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
- final long ident = Binder.clearCallingIdentity();
- try {
- if (args.length > 0 && "--proto".equals(args[0])) {
- dumpProto(fd);
- } else {
- dumpInternal(pw);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override // Binder call
- public void resetTimeout(byte [] token) {
- checkPermission(RESET_FINGERPRINT_LOCKOUT);
- // TODO: confirm security token when we move timeout management into the HAL layer.
- mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
- }
-
- @Override
- public void addLockoutResetCallback(final IFingerprintServiceLockoutResetCallback callback)
- throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- addLockoutResetMonitor(
- new FingerprintServiceLockoutResetMonitor(callback));
- }
- });
- }
-
- @Override
- public boolean isClientActive() {
- checkPermission(MANAGE_FINGERPRINT);
- synchronized(FingerprintService.this) {
- return (mCurrentClient != null) || (mPendingClient != null);
- }
- }
-
- @Override
- public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
- checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.add(callback);
- }
-
- @Override
- public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
- checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.remove(callback);
- }
- }
-
- private void dumpInternal(PrintWriter pw) {
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", "Fingerprint Manager");
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
- PerformanceStats stats = mPerformanceMap.get(userId);
- PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", N);
- set.put("accept", (stats != null) ? stats.accept : 0);
- set.put("reject", (stats != null) ? stats.reject : 0);
- set.put("acquire", (stats != null) ? stats.acquire : 0);
- set.put("lockout", (stats != null) ? stats.lockout : 0);
- set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
- // cryptoStats measures statistics about secure fingerprint transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
- set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
- set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
- set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
- set.put("permanentLockoutCrypto",
- (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- }
-
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
-
- proto.write(FingerprintUserStatsProto.USER_ID, userId);
- proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
- mFingerprintUtils.getFingerprintsForUser(mContext, userId).size());
-
- // Normal fingerprint authentications (e.g. lockscreen)
- final PerformanceStats normal = mPerformanceMap.get(userId);
- if (normal != null) {
- final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
- proto.write(PerformanceStatsProto.ACCEPT, normal.accept);
- proto.write(PerformanceStatsProto.REJECT, normal.reject);
- proto.write(PerformanceStatsProto.ACQUIRE, normal.acquire);
- proto.write(PerformanceStatsProto.LOCKOUT, normal.lockout);
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, normal.permanentLockout);
- proto.end(countsToken);
- }
-
- // Statistics about secure fingerprint transactions (e.g. to unlock password
- // storage, make secure purchases, etc.)
- final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
- if (crypto != null) {
- final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
- proto.write(PerformanceStatsProto.ACCEPT, crypto.accept);
- proto.write(PerformanceStatsProto.REJECT, crypto.reject);
- proto.write(PerformanceStatsProto.ACQUIRE, crypto.acquire);
- proto.write(PerformanceStatsProto.LOCKOUT, crypto.lockout);
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, crypto.permanentLockout);
- proto.end(countsToken);
- }
-
- proto.end(userToken);
- }
- proto.flush();
- mPerformanceMap.clear();
- mCryptoPerformanceMap.clear();
- }
-
- @Override
- public void onStart() {
- publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
- listenForUserSwitches();
- }
-
- private void updateActiveGroup(int userId, String clientPackage) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
-
- if (daemon != null) {
- try {
- userId = getUserOrWorkProfileId(clientPackage, userId);
- if (userId != mCurrentUserId) {
- int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
- if (firstSdkInt < Build.VERSION_CODES.BASE) {
- Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
- "at least VERSION_CODES.BASE");
- }
- File baseDir;
- if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
- baseDir = Environment.getUserSystemDirectory(userId);
- } else {
- baseDir = Environment.getDataVendorDeDirectory(userId);
- }
-
- File fpDir = new File(baseDir, FP_DATA_DIR);
- if (!fpDir.exists()) {
- if (!fpDir.mkdir()) {
- Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
- return;
- }
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(fpDir)) {
- Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
- return;
- }
- }
-
- daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
- mCurrentUserId = userId;
- }
- mAuthenticatorIds.put(userId,
- hasEnrolledFingerprints(userId) ? daemon.getAuthenticatorId() : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveGroup():", e);
- }
- }
- }
-
- /**
- * @param clientPackage the package of the caller
- * @return the profile id
- */
- private int getUserOrWorkProfileId(String clientPackage, int userId) {
- if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
- return userId;
- }
- return getEffectiveUserId(userId);
- }
-
- /**
- * @param userId
- * @return true if this is a work profile
- */
- private boolean isWorkProfile(int userId) {
- UserInfo userInfo = null;
- final long token = Binder.clearCallingIdentity();
- try {
- userInfo = mUserManager.getUserInfo(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return userInfo != null && userInfo.isManagedProfile();
- }
-
- private void listenForUserSwitches() {
- try {
- ActivityManager.getService().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) throws RemoteException {
- mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
- .sendToTarget();
- }
- }, TAG);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to listen for user switching event" ,e);
- }
- }
-
- /***
- * @param opPackageName the name of the calling package
- * @return authenticator id for the calling user
- */
- public long getAuthenticatorId(String opPackageName) {
- final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
- return mAuthenticatorIds.getOrDefault(userId, 0L);
- }
-}
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
deleted file mode 100644
index 434db98..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.util.Slog;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An internal class to help clean up unknown fingerprints in the hardware and software
- */
-public abstract class InternalEnumerateClient extends EnumerateClient {
-
- private List<Fingerprint> mEnrolledList;
- private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
-
- public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int groupId, int userId,
- boolean restricted, String owner, List<Fingerprint> enrolledList) {
-
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mEnrolledList = enrolledList;
- }
-
- private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {
-
- boolean matched = false;
- for (int i=0; i<mEnrolledList.size(); i++) {
- if (mEnrolledList.get(i).getFingerId() == fingerId) {
- mEnrolledList.remove(i);
- matched = true;
- break;
- }
- }
-
- // fingerId 0 means no fingerprints are in hardware
- if (!matched && fingerId != 0) {
- Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
- mUnknownFingerprints.add(fingerprint);
- }
- }
-
- private void doFingerprintCleanup() {
-
- if (mEnrolledList == null) {
- return;
- }
-
- for (Fingerprint f : mEnrolledList) {
- Slog.e(TAG, "Internal Enumerate: Removing dangling enrolled fingerprint: "
- + f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
- + " " + f.getDeviceId());
-
- FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
- f.getFingerId(), getTargetUserId());
- }
- mEnrolledList.clear();
- }
-
- public List<Fingerprint> getUnknownFingerprints() {
- return mUnknownFingerprints;
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-
- handleEnumeratedFingerprint(fingerId, groupId, remaining);
- if (remaining == 0) {
- doFingerprintCleanup();
- }
-
- return remaining == 0;
- }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
deleted file mode 100644
index ffc8488..0000000
--- a/services/core/java/com/android/server/fingerprint/RemovalClient.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-
-/**
- * A class to keep track of the remove state for a given client.
- */
-public abstract class RemovalClient extends ClientMonitor {
- private int mFingerId;
-
- public RemovalClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
- boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mFingerId = fingerId;
- }
-
- @Override
- public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- // The fingerprint template ids will be removed when we get confirmation from the HAL
- try {
- final int result = daemon.remove(getGroupId(), mFingerId);
- if (result != 0) {
- Slog.w(TAG, "startRemove with id = " + mFingerId + " failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingerprintd_remove_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startRemove failed", e);
- }
- return 0;
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopRemove: already cancelled!");
- return 0;
- }
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopRemoval: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- try {
- final int result = daemon.cancel();
- if (result != 0) {
- Slog.w(TAG, "stopRemoval failed, result=" + result);
- return result;
- }
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
- } catch (RemoteException e) {
- Slog.e(TAG, "stopRemoval failed", e);
- return ERROR_ESRCH;
- }
- mAlreadyCancelled = true;
- return 0; // success
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendRemoved(int fingerId, int groupId, int remaining) {
- IFingerprintServiceReceiver receiver = getReceiver();
- try {
- if (receiver != null) {
- receiver.onRemoved(getHalDeviceId(), fingerId, groupId, remaining);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify Removed:", e);
- }
- return remaining == 0;
- }
-
- @Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (fingerId != 0) {
- FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
- getTargetUserId());
- }
- return sendRemoved(fingerId, getGroupId(), remaining);
- }
-
- @Override
- public boolean onEnrollResult(int fingerId, int groupId, int rem) {
- if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
- return true; // Invalid for Remove
- }
-
- @Override
- public boolean onAuthenticated(int fingerId, int groupId) {
- if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
- return true; // Invalid for Remove.
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for remove!");
- return true; // Invalid for Remove.
- }
-
-
-}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 5681367..2949b92 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -516,8 +516,7 @@
static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
byte[] params = message.getParams();
return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
- && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
- || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
+ && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d515ce6..86ecd12 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -254,7 +254,7 @@
// 1 second, or 1 Hz frequency.
private static final long LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS = 1000;
// Default update duration in milliseconds for REQUEST_LOCATION.
- private static final long LOCATION_UPDATE_DURATION_MILLIS = 0;
+ private static final long LOCATION_UPDATE_DURATION_MILLIS = 10 * 1000;
/** simpler wrapper for ProviderRequest + Worksource */
private static class GpsRequest {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a7df0e2..3eb7321 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -414,7 +414,9 @@
}
// We don't need to change a2dp status when bluetooth is not connected.
if (btDevice != null) {
- Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
+ if (DEBUG) {
+ Slog.d(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
+ }
mAudioService.setBluetoothA2dpOn(a2dpOn);
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0897b8a..c45d861 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3408,20 +3408,27 @@
@Override
public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
- Adjustment adjustment) throws RemoteException {
+ Adjustment adjustment) {
+ boolean foundEnqueued = false;
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationLock) {
mAssistants.checkServiceTokenLocked(token);
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
- final NotificationRecord n = mEnqueuedNotifications.get(i);
- if (Objects.equals(adjustment.getKey(), n.getKey())
- && Objects.equals(adjustment.getUser(), n.getUserId())) {
- applyAdjustment(n, adjustment);
+ final NotificationRecord r = mEnqueuedNotifications.get(i);
+ if (Objects.equals(adjustment.getKey(), r.getKey())
+ && Objects.equals(adjustment.getUser(), r.getUserId())) {
+ applyAdjustment(r, adjustment);
+ r.applyAdjustments();
+ foundEnqueued = true;
break;
}
}
+ if (!foundEnqueued) {
+ // adjustment arrived too late to apply to enqueued; apply to posted
+ applyAdjustmentFromAssistant(token, adjustment);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3430,7 +3437,7 @@
@Override
public void applyAdjustmentFromAssistant(INotificationListener token,
- Adjustment adjustment) throws RemoteException {
+ Adjustment adjustment) {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationLock) {
@@ -3446,7 +3453,7 @@
@Override
public void applyAdjustmentsFromAssistant(INotificationListener token,
- List<Adjustment> adjustments) throws RemoteException {
+ List<Adjustment> adjustments) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -4525,6 +4532,15 @@
@GuardedBy("mNotificationLock")
@VisibleForTesting
protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+ // Ignore summary updates because we don't display most of the information.
+ if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: summary");
+ }
+ return false;
+ }
+
if (old == null) {
if (DEBUG_INTERRUPTIVENESS) {
Log.v(TAG, "INTERRUPTIVENESS: "
@@ -4562,15 +4578,6 @@
return false;
}
- // Ignore summary updates because we don't display most of the information.
- if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
- if (DEBUG_INTERRUPTIVENESS) {
- Log.v(TAG, "INTERRUPTIVENESS: "
- + r.getKey() + " is not interruptive: summary");
- }
- return false;
- }
-
final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
if (!Objects.equals(oldTitle, newTitle)) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 39d0bf5..75b9f13 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -128,6 +128,11 @@
// The most recent update time, or the creation time if no updates.
private long mUpdateTimeMs;
+ // The most recent interruption time, or the creation time if no updates. Differs from the
+ // above value because updates are filtered based on whether they actually interrupted the
+ // user
+ private long mInterruptionTimeMs;
+
// Is this record an update of an old record?
public boolean isUpdate;
private int mPackagePriority;
@@ -180,6 +185,7 @@
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
+ mInterruptionTimeMs = mCreationTimeMs;
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
@@ -525,6 +531,7 @@
pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
+ pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
if (mPreChannelsNotification) {
pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
@@ -786,6 +793,10 @@
return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
}
+ public int getInterruptionMs(long now) {
+ return (int) (now - mInterruptionTimeMs);
+ }
+
/**
* Set the visibility of the notification.
*/
@@ -844,7 +855,7 @@
public void setSeen() {
mStats.setSeen();
if (mTextChanged) {
- mIsInterruptive = true;
+ setInterruptive(true);
}
}
@@ -940,6 +951,17 @@
public void setInterruptive(boolean interruptive) {
mIsInterruptive = interruptive;
+ final long now = System.currentTimeMillis();
+ mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs;
+
+ if (interruptive) {
+ MetricsLogger.action(getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
+ getInterruptionMs(now)));
+ MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now));
+ }
}
public void setTextChanged(boolean textChanged) {
@@ -1116,7 +1138,9 @@
sbn.getNotification().isGroupSummary() ? 1 : 0)
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
- .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
+ .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
+ .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
+ getInterruptionMs(now));
}
public LogMaker getLogMaker() {
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 97f6aa2..65ccecd 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.ArrayList;
import java.util.List;
@@ -112,7 +113,7 @@
launchIntent.setPackage(null);
launchIntent.setComponent(component);
- mInjector.getActivityManagerInternal().startActivityAsUser(
+ mInjector.getActivityTaskManagerInternal().startActivityAsUser(
caller, callingPackage, launchIntent,
ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
user.getIdentifier());
@@ -241,6 +242,11 @@
public ActivityManagerInternal getActivityManagerInternal() {
return LocalServices.getService(ActivityManagerInternal.class);
}
+
+ @Override
+ public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+ return LocalServices.getService(ActivityTaskManagerInternal.class);
+ }
}
@VisibleForTesting
@@ -264,5 +270,7 @@
AppOpsManager getAppOpsManager();
ActivityManagerInternal getActivityManagerInternal();
+
+ ActivityTaskManagerInternal getActivityTaskManagerInternal();
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index feac8e6..9323040 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -63,6 +63,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.Collections;
import java.util.List;
@@ -107,6 +108,7 @@
private final UserManager mUm;
private final UserManagerInternal mUserManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final ShortcutServiceInternal mShortcutServiceInternal;
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
@@ -122,6 +124,8 @@
LocalServices.getService(UserManagerInternal.class));
mActivityManagerInternal = Preconditions.checkNotNull(
LocalServices.getService(ActivityManagerInternal.class));
+ mActivityTaskManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ActivityTaskManagerInternal.class));
mShortcutServiceInternal = Preconditions.checkNotNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
@@ -521,7 +525,7 @@
@NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
final int code;
try {
- code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage,
+ code = mActivityTaskManagerInternal.startActivitiesAsPackage(publisherPackage,
userId, intents, startActivityOptions);
if (ActivityManager.isStartResultSuccessful(code)) {
return true; // Success
@@ -618,7 +622,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mActivityManagerInternal.startActivityAsUser(caller, callingPackage,
+ mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
launchIntent, opts, user.getIdentifier());
}
@@ -641,7 +645,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mActivityManagerInternal.startActivityAsUser(caller, callingPackage,
+ mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
intent, opts, user.getIdentifier());
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e4567a8..9a15623 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -60,7 +60,6 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
@@ -91,12 +90,10 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
-
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -322,11 +319,7 @@
import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import dalvik.system.CloseGuard;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -372,6 +365,10 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
+import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
/**
* Keep track of all those APKs everywhere.
* <p>
@@ -451,10 +448,6 @@
private static final int SHELL_UID = Process.SHELL_UID;
private static final int SE_UID = Process.SE_UID;
- // Suffix used during package installation when copying/moving
- // package apks to install directory.
- private static final String INSTALL_PACKAGE_SUFFIX = "-";
-
static final int SCAN_NO_DEX = 1<<0;
static final int SCAN_UPDATE_SIGNATURE = 1<<1;
static final int SCAN_NEW_INSTALL = 1<<2;
@@ -692,11 +685,7 @@
// as the lock for the global state. Methods that must be called with
// this lock held have the prefix "LP".
@GuardedBy("mPackages")
- final ArrayMap<String, PackageParser.Package> mPackages =
- new ArrayMap<String, PackageParser.Package>();
-
- final ArrayMap<String, Set<String>> mKnownCodebase =
- new ArrayMap<String, Set<String>>();
+ final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
// Keys are isolated uids and values are the uid of the application
// that created the isolated proccess.
@@ -795,18 +784,15 @@
for (PackageParser.Package p : allPackages) {
if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
if (overlayPackages == null) {
- overlayPackages = new ArrayList<PackageParser.Package>();
+ overlayPackages = new ArrayList<>();
}
overlayPackages.add(p);
}
}
if (overlayPackages != null) {
- Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
- public int compare(PackageParser.Package p1, PackageParser.Package p2) {
- return p1.mOverlayPriority - p2.mOverlayPriority;
- }
- };
- Collections.sort(overlayPackages, cmp);
+ Comparator<PackageParser.Package> cmp =
+ Comparator.comparingInt(p -> p.mOverlayPriority);
+ overlayPackages.sort(cmp);
}
return overlayPackages;
}
@@ -820,7 +806,7 @@
for (PackageParser.Package overlayPackage : overlayPackages) {
if (targetPath == null) {
if (overlayPathList == null) {
- overlayPathList = new ArrayList<String>();
+ overlayPathList = new ArrayList<>();
}
overlayPathList.add(overlayPackage.baseCodePath);
continue;
@@ -836,7 +822,7 @@
UserHandle.getSharedAppGid(
UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
if (overlayPathList == null) {
- overlayPathList = new ArrayList<String>();
+ overlayPathList = new ArrayList<>();
}
overlayPathList.add(overlayPackage.baseCodePath);
} catch (InstallerException e) {
@@ -878,7 +864,7 @@
for (PackageParser.Package p : mPackages.values()) {
if (p.mOverlayIsStatic) {
if (mOverlayPackages == null) {
- mOverlayPackages = new ArrayList<PackageParser.Package>();
+ mOverlayPackages = new ArrayList<>();
}
mOverlayPackages.add(p);
}
@@ -939,24 +925,22 @@
// Mapping from provider base names (first directory in content URI codePath)
// to the provider information.
- final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority =
- new ArrayMap<String, PackageParser.Provider>();
+ final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = new ArrayMap<>();
// Mapping from instrumentation class names to info about them.
final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
- new ArrayMap<ComponentName, PackageParser.Instrumentation>();
+ new ArrayMap<>();
// Packages whose data we have transfered into another package, thus
// should no longer exist.
- final ArraySet<String> mTransferedPackages = new ArraySet<String>();
+ final ArraySet<String> mTransferedPackages = new ArraySet<>();
// Broadcast actions that are only available to the system.
@GuardedBy("mProtectedBroadcasts")
final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
/** List of packages waiting for verification. */
- final SparseArray<PackageVerificationState> mPendingVerification
- = new SparseArray<PackageVerificationState>();
+ final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
final PackageInstallerService mInstallerService;
@@ -1007,7 +991,7 @@
final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
- = new SparseArray<IntentFilterVerificationState>();
+ = new SparseArray<>();
// TODO remove this and go through mPermissonManager directly
final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
@@ -1018,6 +1002,7 @@
private UserManagerInternal mUserManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
+ private ActivityTaskManagerInternal mActivityTaskManagerInternal;
private DeviceIdleController.LocalService mDeviceIdleController;
@@ -1036,7 +1021,6 @@
pkg = _pkg;
replacing = _replacing;
userId = _userId;
- replacing = _replacing;
verifierUid = _verifierUid;
}
}
@@ -1051,7 +1035,7 @@
private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
private Context mContext;
private ComponentName mIntentFilterVerifierComponent;
- private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+ private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
mContext = context;
@@ -1141,7 +1125,7 @@
mIntentFilterVerificationStates.remove(verificationId);
final String packageName = ivs.getPackageName();
- IntentFilterVerificationInfo ivi = null;
+ IntentFilterVerificationInfo ivi;
synchronized (mPackages) {
ivi = mSettings.getIntentFilterVerificationLPr(packageName);
@@ -1246,7 +1230,7 @@
final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
public PendingPackageBroadcasts() {
- mUidMap = new SparseArray<ArrayMap<String, ArrayList<String>>>(2);
+ mUidMap = new SparseArray<>(2);
}
public ArrayList<String> get(int userId, String packageName) {
@@ -1298,7 +1282,7 @@
private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
if (map == null) {
- map = new ArrayMap<String, ArrayList<String>>();
+ map = new ArrayMap<>();
mUidMap.put(userId, map);
}
return map;
@@ -1313,11 +1297,9 @@
static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
- static final int END_COPY = 4;
static final int INIT_COPY = 5;
static final int MCS_UNBIND = 6;
static final int START_CLEANING_PACKAGE = 7;
- static final int FIND_INSTALL_LOC = 8;
static final int POST_INSTALL = 9;
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
@@ -1342,7 +1324,7 @@
static UserManagerService sUserManager;
// Stores a list of users whose package restrictions file needs to be updated
- private ArraySet<Integer> mDirtyUsers = new ArraySet<Integer>();
+ private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
@@ -1371,7 +1353,7 @@
}
}
- final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
+ final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
// XML tags for backup/restore of various bits of state
@@ -1418,7 +1400,7 @@
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls =
- new ArrayList<HandlerParams>();
+ new ArrayList<>();
private boolean connectToService() {
if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
@@ -1624,9 +1606,6 @@
int uids[];
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
- if (mPendingBroadcasts == null) {
- return;
- }
size = mPendingBroadcasts.size();
if (size <= 0) {
// Nothing to be done. Just return
@@ -1901,12 +1880,7 @@
private PermissionCallback mPermissionCallback = new PermissionCallback() {
@Override
public void onGidsChanged(int appId, int userId) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
- }
- });
+ mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
}
@Override
public void onPermissionGranted(int uid, int userId) {
@@ -2546,13 +2520,11 @@
requestCopyPreoptedFiles();
}
- String customResolverActivity = Resources.getSystem().getString(
+ String customResolverActivityName = Resources.getSystem().getString(
R.string.config_customResolverActivity);
- if (TextUtils.isEmpty(customResolverActivity)) {
- customResolverActivity = null;
- } else {
+ if (!TextUtils.isEmpty(customResolverActivityName)) {
mCustomResolverComponentName = ComponentName.unflattenFromString(
- customResolverActivity);
+ customResolverActivityName);
}
long startTime = SystemClock.uptimeMillis();
@@ -3402,7 +3374,7 @@
}
}
if (ret != PackageManager.INSTALL_SUCCEEDED) {
- if (dstCodePath == null || !dstCodePath.exists()) {
+ if (!dstCodePath.exists()) {
return null;
}
removeCodePathLI(dstCodePath);
@@ -3753,7 +3725,7 @@
for (ActivityIntentInfo filter : a.intents) {
if (hasValidDomains(filter)) {
if (domains == null) {
- domains = new ArraySet<String>();
+ domains = new ArraySet<>();
}
domains.addAll(filter.getHostsList());
}
@@ -3820,7 +3792,7 @@
PackageManager.MATCH_ALL, userId);
final int count = list.size();
- List<String> result = new ArrayList<String>(count);
+ List<String> result = new ArrayList<>(count);
for (int i=0; i<count; i++) {
ResolveInfo info = list.get(i);
if (info.activityInfo == null
@@ -3875,16 +3847,6 @@
}
}
- static int[] appendInts(int[] cur, int[] add) {
- if (add == null) return cur;
- if (cur == null) return add;
- final int N = add.length;
- for (int i=0; i<N; i++) {
- cur = appendInt(cur, add[i]);
- }
- return cur;
- }
-
/**
* Returns whether or not a full application can see an instant application.
* <p>
@@ -3948,7 +3910,7 @@
? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
// Compute granted permissions only if package has requested permissions
final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
- ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
+ ? Collections.emptySet() : permissionsState.getPermissions(userId);
PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
@@ -4767,6 +4729,13 @@
return mActivityManagerInternal;
}
+ private ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+ if (mActivityTaskManagerInternal == null) {
+ mActivityTaskManagerInternal =
+ LocalServices.getService(ActivityTaskManagerInternal.class);
+ }
+ return mActivityTaskManagerInternal;
+ }
private DeviceIdleController.LocalService getDeviceIdleController() {
if (mDeviceIdleController == null) {
@@ -4956,7 +4925,7 @@
}
private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
- if (!getActivityManagerInternal().isCallerRecents(callingUid)) {
+ if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) {
return false;
}
final long token = Binder.clearCallingIdentity();
@@ -5757,7 +5726,7 @@
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mPackages) {
if (canViewInstantApps(callingUid, callingUserId)) {
- return new ArrayList<String>(mPackages.keySet());
+ return new ArrayList<>(mPackages.keySet());
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final List<String> result = new ArrayList<>();
@@ -6629,7 +6598,7 @@
flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/);
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
@@ -6685,7 +6654,7 @@
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
- List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+ List<ResolveInfo> xpResult = new ArrayList<>(1);
xpResult.add(xpResolveInfo);
return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
@@ -7110,12 +7079,12 @@
candidates.size());
}
- ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> alwaysList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> undefinedList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> alwaysAskList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> neverList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> matchAllList = new ArrayList<ResolveInfo>();
+ final ArrayList<ResolveInfo> result = new ArrayList<>();
+ final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
+ final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+ final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
+ final ArrayList<ResolveInfo> neverList = new ArrayList<>();
+ final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
synchronized (mPackages) {
final int count = candidates.size();
@@ -7590,7 +7559,7 @@
}
}
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
@@ -7704,7 +7673,7 @@
}
}
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
// When specifying an explicit component, we prevent the service from being
@@ -7822,7 +7791,7 @@
}
}
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ProviderInfo pi = getProviderInfo(comp, flags, userId);
if (pi != null) {
// When specifying an explicit component, we prevent the provider from being
@@ -8021,7 +7990,7 @@
// writer
synchronized (mPackages) {
- ArrayList<PackageInfo> list = new ArrayList<PackageInfo>();
+ ArrayList<PackageInfo> list = new ArrayList<>();
boolean[] tmpBools = new boolean[permissions.length];
if (listUninstalled) {
for (PackageSetting ps : mSettings.mPackages.values()) {
@@ -8038,7 +8007,7 @@
}
}
- return new ParceledListSlice<PackageInfo>(list);
+ return new ParceledListSlice<>(list);
}
}
@@ -8239,7 +8208,7 @@
}
private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
- final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
// reader
synchronized (mPackages) {
@@ -8368,7 +8337,7 @@
continue;
}
if (finalList == null) {
- finalList = new ArrayList<ProviderInfo>(3);
+ finalList = new ArrayList<>(3);
}
ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
ps.readUserState(userId), userId);
@@ -8380,8 +8349,8 @@
}
if (finalList != null) {
- Collections.sort(finalList, mProviderInitOrderSorter);
- return new ParceledListSlice<ProviderInfo>(finalList);
+ finalList.sort(mProviderInitOrderSorter);
+ return new ParceledListSlice<>(finalList);
}
return ParceledListSlice.emptyList();
@@ -8417,7 +8386,7 @@
private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
int flags) {
- ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>();
+ ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
// reader
synchronized (mPackages) {
@@ -8487,10 +8456,8 @@
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
- if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
- scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
- currentTime, null);
- }
+ scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
+ currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
@@ -8692,7 +8659,7 @@
* match one in a trusted source, and should be done separately.
*/
private boolean canSkipFullApkVerification(String apkPath) {
- byte[] rootHashObserved = null;
+ final byte[] rootHashObserved;
try {
rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
if (rootHashObserved == null) {
@@ -8958,7 +8925,7 @@
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
- private static final void enforceSystemOrRoot(String message) {
+ private static void enforceSystemOrRoot(String message) {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
throw new SecurityException(message);
@@ -9361,7 +9328,7 @@
}
public ArraySet<String> getOptimizablePackages() {
- ArraySet<String> pkgs = new ArraySet<String>();
+ ArraySet<String> pkgs = new ArraySet<>();
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) {
@@ -12937,9 +12904,7 @@
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
- if (info != null) {
- res.handleAllWebDataURI = info.handleAllWebDataURI();
- }
+ res.handleAllWebDataURI = info.handleAllWebDataURI();
res.priority = info.getPriority();
res.preferredOrder = activity.owner.mPreferredOrder;
//System.out.println("Result: " + res.activityInfo.className +
@@ -12961,7 +12926,7 @@
@Override
protected void sortResults(List<ResolveInfo> results) {
- Collections.sort(results, mResolvePrioritySorter);
+ results.sort(mResolvePrioritySorter);
}
@Override
@@ -12994,7 +12959,7 @@
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
- = new ArrayMap<ComponentName, PackageParser.Activity>();
+ = new ArrayMap<>();
private int mFlags;
}
@@ -13024,8 +12989,7 @@
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageServices.size();
- ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
- new ArrayList<PackageParser.ServiceIntentInfo[]>(N);
+ ArrayList<PackageParser.ServiceIntentInfo[]> listCut = new ArrayList<>(N);
ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
@@ -13178,7 +13142,7 @@
@Override
protected void sortResults(List<ResolveInfo> results) {
- Collections.sort(results, mResolvePrioritySorter);
+ results.sort(mResolvePrioritySorter);
}
@Override
@@ -13227,8 +13191,7 @@
// }
// Keys are String (activity class name), values are Activity.
- private final ArrayMap<ComponentName, PackageParser.Service> mServices
- = new ArrayMap<ComponentName, PackageParser.Service>();
+ private final ArrayMap<ComponentName, PackageParser.Service> mServices = new ArrayMap<>();
private int mFlags;
}
@@ -13260,8 +13223,7 @@
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageProviders.size();
- ArrayList<PackageParser.ProviderIntentInfo[]> listCut =
- new ArrayList<PackageParser.ProviderIntentInfo[]>(N);
+ ArrayList<PackageParser.ProviderIntentInfo[]> listCut = new ArrayList<>(N);
ArrayList<PackageParser.ProviderIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
@@ -13421,7 +13383,7 @@
@Override
protected void sortResults(List<ResolveInfo> results) {
- Collections.sort(results, mResolvePrioritySorter);
+ results.sort(mResolvePrioritySorter);
}
@Override
@@ -13454,7 +13416,7 @@
}
private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
- = new ArrayMap<ComponentName, PackageParser.Provider>();
+ = new ArrayMap<>();
private int mFlags;
}
@@ -13539,78 +13501,69 @@
}
}
- private static final Comparator<ResolveInfo> mResolvePrioritySorter =
- new Comparator<ResolveInfo>() {
- public int compare(ResolveInfo r1, ResolveInfo r2) {
- int v1 = r1.priority;
- int v2 = r2.priority;
- //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
- if (v1 != v2) {
- return (v1 > v2) ? -1 : 1;
- }
- v1 = r1.preferredOrder;
- v2 = r2.preferredOrder;
- if (v1 != v2) {
- return (v1 > v2) ? -1 : 1;
- }
- if (r1.isDefault != r2.isDefault) {
- return r1.isDefault ? -1 : 1;
- }
- v1 = r1.match;
- v2 = r2.match;
- //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
- if (v1 != v2) {
- return (v1 > v2) ? -1 : 1;
- }
- if (r1.system != r2.system) {
- return r1.system ? -1 : 1;
- }
- if (r1.activityInfo != null) {
- return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
- }
- if (r1.serviceInfo != null) {
- return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
- }
- if (r1.providerInfo != null) {
- return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
- }
- return 0;
+ private static final Comparator<ResolveInfo> mResolvePrioritySorter = (r1, r2) -> {
+ int v1 = r1.priority;
+ int v2 = r2.priority;
+ //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
+ if (v1 != v2) {
+ return (v1 > v2) ? -1 : 1;
}
+ v1 = r1.preferredOrder;
+ v2 = r2.preferredOrder;
+ if (v1 != v2) {
+ return (v1 > v2) ? -1 : 1;
+ }
+ if (r1.isDefault != r2.isDefault) {
+ return r1.isDefault ? -1 : 1;
+ }
+ v1 = r1.match;
+ v2 = r2.match;
+ //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
+ if (v1 != v2) {
+ return (v1 > v2) ? -1 : 1;
+ }
+ if (r1.system != r2.system) {
+ return r1.system ? -1 : 1;
+ }
+ if (r1.activityInfo != null) {
+ return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+ }
+ if (r1.serviceInfo != null) {
+ return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+ }
+ if (r1.providerInfo != null) {
+ return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+ }
+ return 0;
};
- private static final Comparator<ProviderInfo> mProviderInitOrderSorter =
- new Comparator<ProviderInfo>() {
- public int compare(ProviderInfo p1, ProviderInfo p2) {
- final int v1 = p1.initOrder;
- final int v2 = p2.initOrder;
- return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
- }
+ private static final Comparator<ProviderInfo> mProviderInitOrderSorter = (p1, p2) -> {
+ final int v1 = p1.initOrder;
+ final int v2 = p2.initOrder;
+ return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
};
@Override
public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds, int[] instantUserIds) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) return;
- final int[] resolvedUserIds;
- if (userIds == null) {
- resolvedUserIds = am.getRunningUserIds();
- } else {
- resolvedUserIds = userIds;
- }
- doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
- resolvedUserIds, false);
- if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
- doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
- instantUserIds, true);
- }
- } catch (RemoteException ex) {
+ mHandler.post(() -> {
+ try {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) return;
+ final int[] resolvedUserIds;
+ if (userIds == null) {
+ resolvedUserIds = am.getRunningUserIds();
+ } else {
+ resolvedUserIds = userIds;
}
+ doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+ resolvedUserIds, false);
+ if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
+ doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+ instantUserIds, true);
+ }
+ } catch (RemoteException ex) {
}
});
}
@@ -14277,25 +14230,22 @@
} else {
action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
- + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
- return;
- }
- final int[] targetUserIds = new int[] {userId};
- for (String packageName : affectedPackages) {
- doSendBroadcast(am, action, null, intentExtras,
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false);
- }
- } catch (RemoteException ex) {
- // Shouldn't happen as AMS is in the same process.
+ mHandler.post(() -> {
+ try {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+ + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+ return;
}
+ final int[] targetUserIds = new int[] {userId};
+ for (String packageName : affectedPackages) {
+ doSendBroadcast(am, action, null, intentExtras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+ targetUserIds, false);
+ }
+ } catch (RemoteException ex) {
+ // Shouldn't happen as AMS is in the same process.
}
});
}
@@ -14543,7 +14493,7 @@
}
final int N = pkgInfo.verifiers.length;
- final List<ComponentName> sufficientVerifiers = new ArrayList<ComponentName>(N + 1);
+ final List<ComponentName> sufficientVerifiers = new ArrayList<>(N + 1);
for (int i = 0; i < N; i++) {
final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
@@ -15053,36 +15003,33 @@
// are coherent. In the non-restore case, the app has already completed install
// and been launched through some other means, so it is not in a problematic
// state for observers to see the FIRST_LAUNCH signal.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < mRunningInstalls.size(); i++) {
- final PostInstallData data = mRunningInstalls.valueAt(i);
- if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- continue;
- }
- if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
- // right package; but is it for the right user?
- for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
- if (userId == data.res.newUsers[uIndex]) {
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Package " + packageName
- + " being restored so deferring FIRST_LAUNCH");
- }
- return;
+ mHandler.post(() -> {
+ for (int i = 0; i < mRunningInstalls.size(); i++) {
+ final PostInstallData data = mRunningInstalls.valueAt(i);
+ if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ continue;
+ }
+ if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
+ // right package; but is it for the right user?
+ for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
+ if (userId == data.res.newUsers[uIndex]) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Package " + packageName
+ + " being restored so deferring FIRST_LAUNCH");
}
+ return;
}
}
}
- // didn't find it, so not being restored
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
- }
- final boolean isInstantApp = isInstantApp(packageName, userId);
- final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
- final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
}
+ // didn't find it, so not being restored
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
+ }
+ final boolean isInstantApp = isInstantApp(packageName, userId);
+ final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+ final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
});
}
@@ -15911,8 +15858,7 @@
}
};
- int ret = PackageManager.INSTALL_SUCCEEDED;
- ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
+ int ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
@@ -16057,17 +16003,6 @@
}
/**
- * Extract the StorageManagerService "container ID" from the full code path of an
- * .apk.
- */
- static String cidFromCodePath(String fullCodePath) {
- int eidx = fullCodePath.lastIndexOf("/");
- String subStr1 = fullCodePath.substring(0, eidx);
- int sidx = subStr1.lastIndexOf("/");
- return subStr1.substring(sidx+1, eidx);
- }
-
- /**
* Logic to handle movement of existing installed applications.
*/
class MoveInstallArgs extends InstallArgs {
@@ -16178,51 +16113,6 @@
}
}
- static String getAsecPackageName(String packageCid) {
- int idx = packageCid.lastIndexOf("-");
- if (idx == -1) {
- return packageCid;
- }
- return packageCid.substring(0, idx);
- }
-
- // Utility method used to create code paths based on package name and available index.
- private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
- String idxStr = "";
- int idx = 1;
- // Fall back to default value of idx=1 if prefix is not
- // part of oldCodePath
- if (oldCodePath != null) {
- String subStr = oldCodePath;
- // Drop the suffix right away
- if (suffix != null && subStr.endsWith(suffix)) {
- subStr = subStr.substring(0, subStr.length() - suffix.length());
- }
- // If oldCodePath already contains prefix find out the
- // ending index to either increment or decrement.
- int sidx = subStr.lastIndexOf(prefix);
- if (sidx != -1) {
- subStr = subStr.substring(sidx + prefix.length());
- if (subStr != null) {
- if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) {
- subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length());
- }
- try {
- idx = Integer.parseInt(subStr);
- if (idx <= 1) {
- idx++;
- } else {
- idx--;
- }
- } catch(NumberFormatException e) {
- }
- }
- }
- }
- idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx);
- return prefix + idxStr;
- }
-
private File getNextCodePath(File targetDir, String packageName) {
File result;
SecureRandom random = new SecureRandom();
@@ -16433,7 +16323,7 @@
// don't allow a system upgrade unless the upgrade hash matches
if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
- byte[] digestBytes = null;
+ final byte[] digestBytes;
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-512");
updateDigest(digest, new File(pkg.baseCodePath));
@@ -16558,18 +16448,10 @@
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
- final boolean oem =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
- final boolean vendor =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
- final boolean product =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+ final boolean privileged = isPrivilegedApp(oldPackage);
+ final boolean oem = isOemApp(oldPackage);
+ final boolean vendor = isVendorApp(oldPackage);
+ final boolean product = isProductApp(oldPackage);
final @ParseFlags int systemParseFlags = parseFlags;
final @ScanFlags int systemScanFlags = scanFlags
| SCAN_AS_SYSTEM
@@ -16620,7 +16502,7 @@
Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
}
final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
- final ArrayList<String> pkgList = new ArrayList<String>(1);
+ final ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(deletedPackage.applicationInfo.packageName);
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
@@ -17846,11 +17728,8 @@
}
private void deleteTempPackageFiles() {
- final FilenameFilter filter = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.startsWith("vmdl") && name.endsWith(".tmp");
- }
- };
+ final FilenameFilter filter =
+ (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
file.delete();
}
@@ -18187,8 +18066,8 @@
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
- PackageSetting uninstalledPs = null;
- PackageParser.Package pkg = null;
+ PackageSetting uninstalledPs;
+ PackageParser.Package pkg;
// for the uninstall-updates case and restricted profiles, remember the per-
// user handle installed state
@@ -18498,13 +18377,10 @@
if (userIdToKill == UserHandle.USER_ALL
|| userIdToKill >= UserHandle.USER_SYSTEM) {
// If gids changed for this user, kill all affected packages.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // This has to happen with no lock held.
- killApplication(deletedPs.name, deletedPs.appId,
- KILL_APP_REASON_GIDS_CHANGED);
- }
+ mHandler.post(() -> {
+ // This has to happen with no lock held.
+ killApplication(deletedPs.name, deletedPs.appId,
+ KILL_APP_REASON_GIDS_CHANGED);
});
break;
}
@@ -19446,12 +19322,8 @@
case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
writeRuntimePermissions = true;
final int appId = ps.appId;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
- }
- });
+ mHandler.post(
+ () -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
} break;
}
}
@@ -19529,33 +19401,31 @@
}
// Queue up an async operation since the package deletion may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
- boolean doClearData = true;
- if (ps != null) {
- final boolean targetIsInstantApp =
- ps.getInstantApp(UserHandle.getUserId(callingUid));
- doClearData = !targetIsInstantApp
- || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+ mHandler.post(() -> {
+ final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
+ boolean doClearData = true;
+ if (ps != null) {
+ final boolean targetIsInstantApp =
+ ps.getInstantApp(UserHandle.getUserId(callingUid));
+ doClearData = !targetIsInstantApp
+ || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+ }
+ if (doClearData) {
+ synchronized (mInstallLock) {
+ final int flags = StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE;
+ // We're only clearing cache files, so we don't care if the
+ // app is unfrozen and still able to run
+ clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+ clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
- if (doClearData) {
- synchronized (mInstallLock) {
- final int flags = StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE;
- // We're only clearing cache files, so we don't care if the
- // app is unfrozen and still able to run
- clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
- clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- }
- clearExternalStorageDataSync(packageName, userId, false);
- }
- if (observer != null) {
- try {
- observer.onRemoveCompleted(packageName, true);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
+ clearExternalStorageDataSync(packageName, userId, false);
+ }
+ if (observer != null) {
+ try {
+ observer.onRemoveCompleted(packageName, true);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
}
}
});
@@ -19838,7 +19708,7 @@
(pa.mPref.mComponent.getPackageName().equals(packageName)
&& pa.mPref.mAlways)) {
if (removed == null) {
- removed = new ArrayList<PreferredActivity>();
+ removed = new ArrayList<>();
}
removed.add(pa);
}
@@ -20002,7 +19872,7 @@
// Mark entry for removal only if it matches the package name.
if (ppa.mComponent.getPackageName().equals(packageName)) {
if (removed == null) {
- removed = new ArrayList<PersistentPreferredActivity>();
+ removed = new ArrayList<>();
}
removed.add(ppa);
}
@@ -20041,7 +19911,6 @@
}
return;
}
-Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
// this is supposed to be TAG_PREFERRED_BACKUP
if (!expectedStartTag.equals(parser.getName())) {
if (DEBUG_BACKUP) {
@@ -20052,12 +19921,11 @@
// skip interfering stuff, then we're aligned with the backing implementation
while ((type = parser.next()) == XmlPullParser.TEXT) { }
-Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
functor.apply(parser, userId);
}
private interface BlobXmlRestorer {
- public void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
+ void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
}
/**
@@ -20105,15 +19973,11 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- mSettings.readPreferredActivitiesLPw(parser, userId);
- }
+ (readParser, readUserId) -> {
+ synchronized (mPackages) {
+ mSettings.readPreferredActivitiesLPw(readParser, readUserId);
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20166,15 +20030,11 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- mSettings.readDefaultAppsLPw(parser, userId);
- }
+ (parser1, userId1) -> {
+ synchronized (mPackages) {
+ mSettings.readDefaultAppsLPw(parser1, userId1);
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
@@ -20222,16 +20082,12 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- mSettings.readAllDomainVerificationsLPr(parser, userId);
- mSettings.writeLPr();
- }
+ (parser1, userId1) -> {
+ synchronized (mPackages) {
+ mSettings.readAllDomainVerificationsLPr(parser1, userId1);
+ mSettings.writeLPr();
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20279,15 +20135,11 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- processRestoredPermissionGrantsLPr(parser, userId);
- }
+ (parser1, userId1) -> {
+ synchronized (mPackages) {
+ processRestoredPermissionGrantsLPr(parser1, userId1);
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20475,7 +20327,7 @@
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
ArraySet<CrossProfileIntentFilter> set =
- new ArraySet<CrossProfileIntentFilter>(resolver.filterSet());
+ new ArraySet<>(resolver.filterSet());
for (CrossProfileIntentFilter filter : set) {
if (filter.getOwnerPackage().equals(ownerPackage)) {
resolver.removeFilter(filter);
@@ -20580,9 +20432,7 @@
allHomeCandidates.clear();
if (list != null) {
- for (ResolveInfo ri : list) {
- allHomeCandidates.add(ri);
- }
+ allHomeCandidates.addAll(list);
}
return (preferred == null || preferred.activityInfo == null)
? null
@@ -20712,7 +20562,6 @@
boolean isApp = (className == null);
final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
String componentName = isApp ? packageName : className;
- int packageUid = -1;
ArrayList<String> components;
// reader
@@ -20952,7 +20801,7 @@
components = mPendingBroadcasts.get(userId, packageName);
final boolean newPackage = components == null;
if (newPackage) {
- components = new ArrayList<String>();
+ components = new ArrayList<>();
}
if (!components.contains(componentName)) {
components.add(componentName);
@@ -20976,7 +20825,7 @@
long callingId = Binder.clearCallingIdentity();
try {
if (sendNow) {
- packageUid = UserHandle.getUid(userId, pkgSetting.appId);
+ int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
sendPackageChangedBroadcast(packageName,
(flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
}
@@ -21159,7 +21008,7 @@
// possible for the user flow to never be able to return to that
// situation so here we do a sanity check to make sure we haven't
// left any junk around.
- ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
+ ArrayList<PreferredActivity> removed = new ArrayList<>();
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
removed.clear();
@@ -21287,16 +21136,16 @@
}
static String arrayToString(int[] array) {
- StringBuffer buf = new StringBuffer(128);
- buf.append('[');
+ StringBuilder stringBuilder = new StringBuilder(128);
+ stringBuilder.append('[');
if (array != null) {
for (int i=0; i<array.length; i++) {
- if (i > 0) buf.append(", ");
- buf.append(array[i]);
+ if (i > 0) stringBuilder.append(", ");
+ stringBuilder.append(array[i]);
}
}
- buf.append(']');
- return buf.toString();
+ stringBuilder.append(']');
+ return stringBuilder.toString();
}
@Override
@@ -22007,7 +21856,7 @@
ipw.println();
ipw.println("Dexopt state:");
ipw.increaseIndent();
- Collection<PackageParser.Package> packages = null;
+ Collection<PackageParser.Package> packages;
if (packageName != null) {
PackageParser.Package targetPackage = mPackages.get(packageName);
if (targetPackage != null) {
@@ -22034,7 +21883,7 @@
ipw.println();
ipw.println("Compiler stats:");
ipw.increaseIndent();
- Collection<PackageParser.Package> packages = null;
+ Collection<PackageParser.Package> packages;
if (packageName != null) {
PackageParser.Package targetPackage = mPackages.get(packageName);
if (targetPackage != null) {
@@ -22069,9 +21918,7 @@
ArraySet<String> result = new ArraySet<>();
if (iviList.size() > 0) {
for (IntentFilterVerificationInfo ivi : iviList) {
- for (String host : ivi.getDomains()) {
- result.add(host);
- }
+ result.addAll(ivi.getDomains());
}
}
if (filters != null && filters.size() > 0) {
@@ -22163,12 +22010,7 @@
}
private void loadPrivatePackages(final VolumeInfo vol) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- loadPrivatePackagesInner(vol);
- }
- });
+ mHandler.post(() -> loadPrivatePackagesInner(vol));
}
private void loadPrivatePackagesInner(VolumeInfo vol) {
@@ -22259,12 +22101,7 @@
}
private void unloadPrivatePackages(final VolumeInfo vol) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- unloadPrivatePackagesInner(vol);
- }
- });
+ mHandler.post(() -> unloadPrivatePackagesInner(vol));
}
private void unloadPrivatePackagesInner(VolumeInfo vol) {
@@ -22319,23 +22156,6 @@
}
}
- private void assertPackageKnown(String volumeUuid, String packageName)
- throws PackageManagerException {
- synchronized (mPackages) {
- // Normalize package name to handle renamed packages
- packageName = normalizePackageNameLPr(packageName);
-
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- throw new PackageManagerException("Package " + packageName + " is unknown");
- } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
- throw new PackageManagerException(
- "Package " + packageName + " found on unknown volume " + volumeUuid
- + "; expected volume " + ps.volumeUuid);
- }
- }
- }
-
private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
synchronized (mPackages) {
@@ -22448,7 +22268,7 @@
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps.
- * @returns list of skipped non-core packages (if {@code onlyCoreApps} is true)
+ * @return list of skipped non-core packages (if {@code onlyCoreApps} is true)
*/
private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
boolean migrateAppData, boolean onlyCoreApps) {
@@ -22818,10 +22638,7 @@
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
+ mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
@@ -22865,15 +22682,12 @@
final int callingUid = Binder.getCallingUid();
final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
final int moveId = mNextMoveId.getAndIncrement();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to move " + packageName, e);
- mMoveCallbacks.notifyStatusChanged(moveId, e.error);
- }
+ mHandler.post(() -> {
+ try {
+ movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to move " + packageName, e);
+ mMoveCallbacks.notifyStatusChanged(moveId, e.error);
}
});
return moveId;
@@ -23073,24 +22887,21 @@
final MoveInfo move;
if (moveCompleteApp) {
// Kick off a thread to report progress estimates
- new Thread() {
- @Override
- public void run() {
- while (true) {
- try {
- if (installedLatch.await(1, TimeUnit.SECONDS)) {
- break;
- }
- } catch (InterruptedException ignored) {
+ new Thread(() -> {
+ while (true) {
+ try {
+ if (installedLatch.await(1, TimeUnit.SECONDS)) {
+ break;
}
-
- final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
- final int progress = 10 + (int) MathUtils.constrain(
- ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
- mMoveCallbacks.notifyStatusChanged(moveId, progress);
+ } catch (InterruptedException ignored) {
}
+
+ final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
+ final int progress = 10 + (int) MathUtils.constrain(
+ ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
+ mMoveCallbacks.notifyStatusChanged(moveId, progress);
}
- }.start();
+ }).start();
final String dataAppName = codeFile.getName();
move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
@@ -23244,12 +23055,9 @@
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, " Removing package " + packageName);
}
- mHandler.post(new Runnable() {
- public void run() {
- deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
- userHandle, 0);
- } //end run
- });
+ //end run
+ mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ userHandle, 0));
}
}
}
@@ -23496,12 +23304,8 @@
// TODO Implement atomic delete if package is unused
// It is currently possible that the package will be deleted even if it is installed
// after this method returns.
- mHandler.post(new Runnable() {
- public void run() {
- deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
- 0, PackageManager.DELETE_ALL_USERS);
- }
- });
+ mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ 0, PackageManager.DELETE_ALL_USERS));
}
}
@@ -24353,11 +24157,9 @@
String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledTelephonyDataServices");
synchronized (mPackages) {
- Binder.withCleanCallingIdentity( () -> {
- mDefaultPermissionPolicy.
- grantDefaultPermissionsToEnabledTelephonyDataServices(
- packageNames, userId);
- });
+ Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+ grantDefaultPermissionsToEnabledTelephonyDataServices(
+ packageNames, userId));
}
}
@@ -24366,11 +24168,9 @@
String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromDisabledTelephonyDataServices");
synchronized (mPackages) {
- Binder.withCleanCallingIdentity( () -> {
- mDefaultPermissionPolicy.
- revokeDefaultPermissionsFromDisabledTelephonyDataServices(
- packageNames, userId);
- });
+ Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+ revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+ packageNames, userId));
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 407cb93..46935f0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -31,7 +31,6 @@
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -72,7 +71,6 @@
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.util.AtomicFile;
@@ -82,7 +80,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -101,8 +98,7 @@
import com.android.server.SystemService;
import com.android.server.am.UserState;
import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import libcore.io.IoUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -125,6 +121,8 @@
import java.util.List;
import java.util.Objects;
+import libcore.io.IoUtils;
+
/**
* Service for {@link UserManager}.
*
@@ -2968,9 +2966,9 @@
new Thread() {
@Override
public void run() {
- // Clean up any ActivityManager state
- LocalServices.getService(ActivityManagerInternal.class)
- .onUserRemoved(userHandle);
+ // Clean up any ActivityTaskManager state
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .onUserStopped(userHandle);
removeUserState(userHandle);
}
}.start();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 32825f7..b99f8d6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -124,7 +124,6 @@
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
-
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -155,7 +154,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.AppOpsManager;
@@ -274,8 +272,8 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.PointerLocationView;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
@@ -285,6 +283,8 @@
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayFrames;
import com.android.server.wm.WindowManagerInternal;
@@ -454,6 +454,7 @@
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
ActivityManagerInternal mActivityManagerInternal;
+ ActivityTaskManagerInternal mActivityTaskManagerInternal;
AutofillManagerInternal mAutofillManagerInternal;
InputManagerInternal mInputManagerInternal;
InputMethodManagerInternal mInputMethodManagerInternal;
@@ -1117,7 +1118,7 @@
if (mWindowSleepToken != null) {
return;
}
- mWindowSleepToken = mActivityManagerInternal.acquireSleepToken("WindowSleepToken",
+ mWindowSleepToken = mActivityTaskManagerInternal.acquireSleepToken("WindowSleepToken",
DEFAULT_DISPLAY);
};
@@ -2001,6 +2002,7 @@
mWindowManagerFuncs = windowManagerFuncs;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -7792,7 +7794,7 @@
private void updateDreamingSleepToken(boolean acquire) {
if (acquire) {
if (mDreamingSleepToken == null) {
- mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken(
+ mDreamingSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
"Dream", DEFAULT_DISPLAY);
}
} else {
@@ -7807,7 +7809,7 @@
private void updateScreenOffSleepToken(boolean acquire) {
if (acquire) {
if (mScreenOffSleepToken == null) {
- mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken(
+ mScreenOffSleepToken = mActivityTaskManagerInternal.acquireSleepToken(
"ScreenOff", DEFAULT_DISPLAY);
}
} else {
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index c3b9841..ea34346 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -197,6 +197,7 @@
public SliceSpec[] getPinnedSpecs(Uri uri, String pkg) throws RemoteException {
verifyCaller(pkg);
enforceAccess(pkg, uri);
+ uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
return getPinnedSlice(uri).getSpecs();
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4413666..8a135b8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -20,6 +20,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
import android.content.BroadcastReceiver;
@@ -130,8 +131,8 @@
private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray();
// set to true only if user can skip bouncer
- @GuardedBy("mUsersUnlockedByFingerprint")
- private final SparseBooleanArray mUsersUnlockedByFingerprint = new SparseBooleanArray();
+ @GuardedBy("mUsersUnlockedByBiometric")
+ private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
private final StrongAuthTracker mStrongAuthTracker;
@@ -440,11 +441,11 @@
boolean secure = mLockPatternUtils.isSecure(id);
boolean trusted = aggregateIsTrusted(id);
boolean showingKeyguard = true;
- boolean fingerprintAuthenticated = false;
+ boolean biometricAuthenticated = false;
if (mCurrentUser == id) {
- synchronized(mUsersUnlockedByFingerprint) {
- fingerprintAuthenticated = mUsersUnlockedByFingerprint.get(id, false);
+ synchronized(mUsersUnlockedByBiometric) {
+ biometricAuthenticated = mUsersUnlockedByBiometric.get(id, false);
}
try {
showingKeyguard = wm.isKeyguardLocked();
@@ -452,7 +453,7 @@
}
}
boolean deviceLocked = secure && showingKeyguard && !trusted &&
- !fingerprintAuthenticated;
+ !biometricAuthenticated;
setDeviceLockedForUser(id, deviceLocked);
}
}
@@ -1021,20 +1022,20 @@
}
@Override
- public void unlockedByFingerprintForUser(int userId) {
+ public void unlockedByBiometricForUser(int userId, BiometricSourceType biometricSource) {
enforceReportPermission();
- synchronized(mUsersUnlockedByFingerprint) {
- mUsersUnlockedByFingerprint.put(userId, true);
+ synchronized(mUsersUnlockedByBiometric) {
+ mUsersUnlockedByBiometric.put(userId, true);
}
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
0 /* arg2 */).sendToTarget();
}
@Override
- public void clearAllFingerprints() {
+ public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
enforceReportPermission();
- synchronized(mUsersUnlockedByFingerprint) {
- mUsersUnlockedByFingerprint.clear();
+ synchronized(mUsersUnlockedByBiometric) {
+ mUsersUnlockedByBiometric.clear();
}
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
0 /* arg2 */).sendToTarget();
@@ -1188,8 +1189,8 @@
synchronized (mTrustUsuallyManagedForUser) {
mTrustUsuallyManagedForUser.delete(userId);
}
- synchronized (mUsersUnlockedByFingerprint) {
- mUsersUnlockedByFingerprint.delete(userId);
+ synchronized (mUsersUnlockedByBiometric) {
+ mUsersUnlockedByBiometric.delete(userId);
}
refreshAgentList(userId);
refreshDeviceLockedForUser(userId);
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 3c2d66b..a16dbb7 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -4,7 +4,6 @@
import android.app.ActivityManagerInternal;
import android.app.Vr2dDisplayProperties;
-import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -13,18 +12,15 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.ImageReader;
-import android.os.Build;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.service.vr.IPersistentVrStateCallbacks;
import android.service.vr.IVrManager;
import android.util.Log;
import android.view.Surface;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -319,7 +315,7 @@
}
private void updateDisplayId(int displayId) {
- mActivityManagerInternal.setVr2dDisplayId(displayId);
+ LocalServices.getService(ActivityTaskManagerInternal.class).setVr2dDisplayId(displayId);
mWindowManagerInternal.setVr2dDisplayId(displayId);
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index faa197e..5c45afc 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -18,14 +18,13 @@
import static android.view.Display.INVALID_DISPLAY;
import android.Manifest;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.ScreenObserver;
+import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.INotificationManager;
-import android.app.Vr2dDisplayProperties;
import android.app.NotificationManager;
-import android.annotation.NonNull;
+import android.app.Vr2dDisplayProperties;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -59,30 +58,30 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
-
-import com.android.server.FgThread;
-import com.android.server.wm.WindowManagerInternal;
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.utils.ManagedApplicationService.PendingEvent;
-import com.android.server.utils.ManagedApplicationService.LogEvent;
-import com.android.server.utils.ManagedApplicationService.LogFormattable;
-import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
+import com.android.server.utils.ManagedApplicationService.LogEvent;
+import com.android.server.utils.ManagedApplicationService.LogFormattable;
+import com.android.server.utils.ManagedApplicationService.PendingEvent;
+import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.StringBuilder;
import java.text.SimpleDateFormat;
-import java.util.Arrays;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -777,7 +776,7 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- LocalServices.getService(ActivityManagerInternal.class)
+ LocalServices.getService(ActivityTaskManagerInternal.class)
.registerScreenObserver(this);
mNotificationManager = INotificationManager.Stub.asInterface(
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
new file mode 100644
index 0000000..3885cf9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -0,0 +1,269 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppProtoEnums;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseIntArray;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.WindowProcessController;
+
+import java.util.List;
+
+/**
+ * Activity Task manager local system service interface.
+ * @hide Only for use within system server
+ */
+public abstract class ActivityTaskManagerInternal {
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
+ * the splash screen.
+ */
+ public static final int APP_TRANSITION_SPLASH_SCREEN =
+ AppProtoEnums.APP_TRANSITION_SPLASH_SCREEN; // 1
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
+ * app windows were drawn
+ */
+ public static final int APP_TRANSITION_WINDOWS_DRAWN =
+ AppProtoEnums.APP_TRANSITION_WINDOWS_DRAWN; // 2
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+ * timeout.
+ */
+ public static final int APP_TRANSITION_TIMEOUT =
+ AppProtoEnums.APP_TRANSITION_TIMEOUT; // 3
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+ * we drew a task snapshot.
+ */
+ public static final int APP_TRANSITION_SNAPSHOT =
+ AppProtoEnums.APP_TRANSITION_SNAPSHOT; // 4
+
+ /**
+ * Type for {@link #notifyAppTransitionStarting}: The transition was started because it was a
+ * recents animation and we only needed to wait on the wallpaper.
+ */
+ public static final int APP_TRANSITION_RECENTS_ANIM =
+ AppProtoEnums.APP_TRANSITION_RECENTS_ANIM; // 5
+
+ /**
+ * The bundle key to extract the assist data.
+ */
+ public static final String ASSIST_KEY_DATA = "data";
+
+ /**
+ * The bundle key to extract the assist structure.
+ */
+ public static final String ASSIST_KEY_STRUCTURE = "structure";
+
+ /**
+ * The bundle key to extract the assist content.
+ */
+ public static final String ASSIST_KEY_CONTENT = "content";
+
+ /**
+ * The bundle key to extract the assist receiver extras.
+ */
+ public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
+
+ public interface ScreenObserver {
+ void onAwakeStateChanged(boolean isAwake);
+ void onKeyguardStateChanged(boolean isShowing);
+ }
+
+ /**
+ * Sleep tokens cause the activity manager to put the top activity to sleep.
+ * They are used by components such as dreams that may hide and block interaction
+ * with underlying activities.
+ */
+ public static abstract class SleepToken {
+
+ /** Releases the sleep token. */
+ public abstract void release();
+ }
+
+ /**
+ * Acquires a sleep token for the specified display with the specified tag.
+ *
+ * @param tag A string identifying the purpose of the token (eg. "Dream").
+ * @param displayId The display to apply the sleep token to.
+ */
+ public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
+
+ /**
+ * Returns home activity for the specified user.
+ *
+ * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
+ */
+ public abstract ComponentName getHomeActivityForUser(int userId);
+
+ public abstract void onLocalVoiceInteractionStarted(IBinder callingActivity,
+ IVoiceInteractionSession mSession,
+ IVoiceInteractor mInteractor);
+
+ /**
+ * Callback for window manager to let activity manager know that we are finally starting the
+ * app transition;
+ *
+ * @param reasons A map from windowing mode to a reason integer why the transition was started,
+ * which must be one of the APP_TRANSITION_* values.
+ * @param timestamp The time at which the app transition started in
+ * {@link SystemClock#uptimeMillis()} timebase.
+ */
+ public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
+
+ /**
+ * Callback for window manager to let activity manager know that the app transition was
+ * cancelled.
+ */
+ public abstract void notifyAppTransitionCancelled();
+
+ /**
+ * Callback for window manager to let activity manager know that the app transition is finished.
+ */
+ public abstract void notifyAppTransitionFinished();
+
+ /**
+ * Returns the top activity from each of the currently visible stacks. The first entry will be
+ * the focused activity.
+ */
+ public abstract List<IBinder> getTopVisibleActivities();
+
+ /**
+ * Callback for window manager to let activity manager know that docked stack changes its
+ * minimized state.
+ */
+ public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
+
+ /**
+ * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
+ *
+ * - DO NOT call it with the calling UID cleared.
+ * - All the necessary caller permission checks must be done at callsites.
+ *
+ * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+ */
+ public abstract int startActivitiesAsPackage(String packageName,
+ int userId, Intent[] intents, Bundle bOptions);
+
+ /**
+ * Start activity {@code intent} without calling user-id check.
+ *
+ * - DO NOT call it with the calling UID cleared.
+ * - The caller must do the calling user ID check.
+ *
+ * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+ */
+ public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
+ Intent intent, @Nullable Bundle options, int userId);
+
+ /**
+ * Called when Keyguard flags might have changed.
+ *
+ * @param callback Callback to run after activity visibilities have been reevaluated. This can
+ * be used from window manager so that when the callback is called, it's
+ * guaranteed that all apps have their visibility updated accordingly.
+ */
+ public abstract void notifyKeyguardFlagsChanged(@Nullable Runnable callback);
+
+ /**
+ * Called when the trusted state of Keyguard has changed.
+ */
+ public abstract void notifyKeyguardTrustedChanged();
+
+ /**
+ * Called after virtual display Id is updated by
+ * {@link com.android.server.vr.Vr2dDisplay} with a specific
+ * {@param vr2dDisplayId}.
+ */
+ public abstract void setVr2dDisplayId(int vr2dDisplayId);
+
+ /**
+ * Set focus on an activity.
+ * @param token The IApplicationToken for the activity
+ */
+ public abstract void setFocusedActivity(IBinder token);
+
+ public abstract void registerScreenObserver(ScreenObserver observer);
+
+ /**
+ * Returns is the caller has the same uid as the Recents component
+ */
+ public abstract boolean isCallerRecents(int callingUid);
+
+ /**
+ * Returns whether the recents component is the home activity for the given user.
+ */
+ public abstract boolean isRecentsComponentHomeActivity(int userId);
+
+ /**
+ * Cancels any currently running recents animation.
+ */
+ public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+
+ /**
+ * This enforces {@code func} can only be called if either the caller is Recents activity or
+ * has {@code permission}.
+ */
+ public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
+
+ /**
+ * Called after the voice interaction service has changed.
+ */
+ public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
+
+ /**
+ * Set a uid that is allowed to bypass stopped app switches, launching an app
+ * whenever it wants.
+ *
+ * @param type Type of the caller -- unique string the caller supplies to identify itself
+ * and disambiguate with other calles.
+ * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type.
+ * @param userId The user it is allowed for.
+ */
+ public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
+
+ /**
+ * Called when a user has been deleted. This can happen during normal device usage
+ * or just at startup, when partially removed users are purged. Any state persisted by the
+ * ActivityManager should be purged now.
+ *
+ * @param userId The user being cleaned up.
+ */
+ public abstract void onUserStopped(int userId);
+ public abstract boolean isGetTasksAllowed(String caller, int callingPid, int callingUid);
+
+ public abstract void onProcessAdded(WindowProcessController proc);
+ public abstract void onProcessRemoved(String name, int uid);
+ public abstract void onCleanUpApplicationRecord(WindowProcessController proc);
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 08d0ae9..fa6079c 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -1377,7 +1378,7 @@
setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
// We can now show all of the drawn windows!
- if (!mService.mOpeningApps.contains(this)) {
+ if (!mService.mOpeningApps.contains(this) && canShowWindows()) {
showAllWindowsLocked();
}
}
@@ -2270,4 +2271,21 @@
boolean isClosingOrEnteringPip() {
return (isAnimating() && hiddenRequested) || mWillCloseOrEnterPip;
}
+
+ /**
+ * @return Whether we are allowed to show non-starting windows at the moment. We disallow
+ * showing windows during transitions in case we have windows that have wide-color-gamut
+ * color mode set to avoid jank in the middle of the transition.
+ */
+ boolean canShowWindows() {
+ return allDrawn && !(isReallyAnimating() && hasNonDefaultColorWindow());
+ }
+
+ /**
+ * @return true if we have a window that has a non-default color mode set; false otherwise.
+ */
+ private boolean hasNonDefaultColorWindow() {
+ return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
+ true /* topToBottom */);
+ }
}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index b2a12be..0a49c13 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -196,6 +196,12 @@
@Override
public void onAnimationStart(Animator animation) {
+ if (!mTarget.isAttached()) {
+ // No point of trying to animate something that isn't attached to the hierarchy
+ // anymore.
+ cancel();
+ }
+
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
+ " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
+ " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 68be4e8..d66b42f 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -65,4 +65,9 @@
*/
void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
boolean moveToFullscreen);
+
+ /** @return True if the target is attached to the window hierarchy. */
+ default boolean isAttached() {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 7df057c..f2ce63c 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -302,6 +302,18 @@
onOverrideConfigurationChanged(mTmpConfig);
}
+ /** Sets the always on top flag for this configuration container.
+ * When you call this function, make sure that the following functions are called as well to
+ * keep proper z-order.
+ * - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
+ * - {@Link ActivityDisplay#positionChildAtTop(ActivityStack)};
+ * */
+ public void setAlwaysOnTop(boolean alwaysOnTop) {
+ mTmpConfig.setTo(getOverrideConfiguration());
+ mTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
+ onOverrideConfigurationChanged(mTmpConfig);
+ }
+
/**
* Returns true if this container is currently in multi-window mode. I.e. sharing the screen
* with another activity.
@@ -513,7 +525,7 @@
return toString();
}
- boolean isAlwaysOnTop() {
+ public boolean isAlwaysOnTop() {
return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0f5b26a..0cbf8a7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -404,7 +404,7 @@
WindowStateAnimator winAnimator = w.mWinAnimator;
final AppWindowToken atoken = w.mAppToken;
if (winAnimator.mDrawState == READY_TO_SHOW) {
- if (atoken == null || atoken.allDrawn) {
+ if (atoken == null || atoken.canShowWindows()) {
if (w.performShowLocked()) {
pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
@@ -1107,10 +1107,7 @@
}
if (rotateSeamlessly) {
- forAllWindows(w -> {
- w.mWinAnimator.seamlesslyRotateWindow(getPendingTransaction(),
- oldRotation, rotation);
- }, true /* traverseTopToBottom */);
+ seamlesslyRotate(getPendingTransaction(), oldRotation, rotation);
}
mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
@@ -1569,6 +1566,11 @@
}
@VisibleForTesting
+ WindowList<TaskStack> getStacks() {
+ return mTaskStackContainers.mChildren;
+ }
+
+ @VisibleForTesting
TaskStack getTopStack() {
return mTaskStackContainers.getTopStack();
}
@@ -3429,21 +3431,44 @@
boolean toTop = requestedPosition == POSITION_TOP;
toTop |= adding ? requestedPosition >= topChildPosition + 1
: requestedPosition >= topChildPosition;
- int targetPosition = requestedPosition;
- if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED && hasPinnedStack()) {
- // The pinned stack is always the top most stack (always-on-top) when it is present.
- TaskStack topStack = mChildren.get(topChildPosition);
- if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) {
- throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
+ if (stack.inPinnedWindowingMode()) {
+ // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
+ // just return the candidate position.
+ return requestedPosition;
+ }
+
+ // We might call mChildren.get() with targetPosition below, but targetPosition might be
+ // POSITION_TOP (INTEGER_MAX). We need to adjust the value to the actual index in the
+ // array.
+ int targetPosition = toTop ? topChildPosition : requestedPosition;
+ // Note that the index we should return varies depending on the value of adding.
+ // When we're adding a new stack the index is the current target position.
+ // When we're positioning an existing stack the index is the position below the target
+ // stack, because WindowContainer#positionAt() first removes element and then adds
+ // it to specified place.
+ if (toTop && adding) {
+ targetPosition++;
+ }
+
+ // Note we might have multiple always on top windows.
+ while (targetPosition >= 0) {
+ int adjustedTargetStackId = adding ? targetPosition - 1 : targetPosition;
+ if (adjustedTargetStackId < 0 || adjustedTargetStackId > topChildPosition) {
+ break;
}
-
- // So, stack is moved just below the pinned stack.
- // When we're adding a new stack the target is the current pinned stack position.
- // When we're positioning an existing stack the target is the position below pinned
- // stack, because WindowContainer#positionAt() first removes element and then adds
- // it to specified place.
- targetPosition = adding ? topChildPosition : topChildPosition - 1;
+ TaskStack targetStack = mChildren.get(adjustedTargetStackId);
+ if (!targetStack.isAlwaysOnTop()) {
+ // We reached a stack that isn't always-on-top.
+ break;
+ }
+ if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
+ // Always on-top non-pinned windowing mode stacks can go anywhere below pinned
+ // stack.
+ break;
+ }
+ // We go one level down, looking for the place on which the new stack can be put.
+ targetPosition--;
}
return targetPosition;
@@ -3535,15 +3560,11 @@
if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
|| isStackVisible(WINDOWING_MODE_FREEFORM)) {
// Apps and their containers are not allowed to specify an orientation while the
- // docked or freeform stack is visible...except for the home stack/task if the
- // docked stack is minimized and it actually set something.
+ // docked or freeform stack is visible...except for the home stack if the docked
+ // stack is minimized and it actually set something and the bounds is different from
+ // the display.
if (mHomeStack != null && mHomeStack.isVisible()
&& mDividerControllerLocked.isMinimizedDock()
- // TODO(b/110159357): Work around to unblock the release for failing test
- // ActivityManagerAppConfigurationTests#testSplitscreenPortraitAppOrientationRequests
- // which shouldn't be failing since home stack shouldn't be visible. We need
- // to dig deeper to see why it is failing. NOTE: Not failing on current
- // master...
&& !(mDividerControllerLocked.isHomeStackResizable()
&& mHomeStack.matchParentBounds())) {
final int orientation = mHomeStack.getOrientation();
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index cd8d677..e817d1a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -651,7 +651,7 @@
if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
&& appTransition != TRANSIT_NONE &&
!AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
- if (mService.mAmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
+ if (mService.mAtmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
// When the home activity is the recents component and we are already minimized,
// then there is nothing to do here since home is already visible
} else {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 2723801..6f5fea9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,13 +16,13 @@
package com.android.server.wm;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
@@ -48,11 +48,14 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.InputMethodManagerInternal;
+
+import com.google.android.collect.Sets;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
-import com.google.android.collect.Sets;
+
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index e6db6d9..76a060e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1703,6 +1703,11 @@
}
}
+ @Override
+ public boolean isAttached() {
+ return mDisplayContent != null;
+ }
+
/**
* Called immediately prior to resizing the tasks at the end of the pinned stack animation.
*/
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 19c5a3d..8fe7063 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -736,6 +736,20 @@
}
/**
+ * Seamlessly rotates the container, by recomputing the location in the new
+ * rotation, and rotating buffers until they are updated for the new rotation.
+ *
+ * @param t the transaction to perform the seamless rotation in
+ * @param oldRotation the rotation we are rotating from
+ * @param newRotation the rotation we are rotating to
+ */
+ void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ mChildren.get(i).seamlesslyRotate(t, oldRotation, newRotation);
+ }
+ }
+
+ /**
* Returns true if this container is opaque and fills all the space made available by its parent
* container.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6686b80..0c65518 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -368,14 +368,25 @@
* to be synchronized with state in WindowManagerService.
* @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
* key is expected to dismiss the software keyboard.
- * @param targetWindowToken token to identify the target window that the IME is associated with.
- * {@code null} when application, system, or the IME itself decided to
- * change its window visibility before being associated with any target
- * window.
*/
public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
- @Nullable IBinder targetWindowToken);
+ boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed);
+
+ /**
+ * Notifies WindowManagerService that the current IME window status is being changed.
+ *
+ * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
+ * caller of this method.</p>
+ *
+ * @param imeToken token to track the active input method. Corresponding IME windows can be
+ * identified by checking {@link android.view.WindowManager.LayoutParams#token}.
+ * Note that there is no guarantee that the corresponding window is already
+ * created
+ * @param imeTargetWindowToken token to identify the target window that the IME is associated
+ * with
+ */
+ public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+ @NonNull IBinder imeTargetWindowToken);
/**
* Returns true when the hardware keyboard is available.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c1753c0..399078a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -344,6 +344,8 @@
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
+ /** Indicates we are removing the focused window when updating the focus. */
+ static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
@@ -432,6 +434,7 @@
// TODO: Probably not needed once activities are fully in WM.
final IActivityTaskManager mActivityTaskManager;
final ActivityManagerInternal mAmInternal;
+ final ActivityTaskManagerInternal mAtmInternal;
final AppOpsManager mAppOps;
final PackageManagerInternal mPmInternal;
@@ -1015,6 +1018,7 @@
mActivityManager = ActivityManager.getService();
mActivityTaskManager = ActivityTaskManager.getService();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
AppOpsManager.OnOpChangedInternalListener opListener =
new AppOpsManager.OnOpChangedInternalListener() {
@@ -4985,16 +4989,16 @@
}
break;
case NOTIFY_APP_TRANSITION_STARTING: {
- mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj,
+ mAtmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj,
msg.getWhen());
}
break;
case NOTIFY_APP_TRANSITION_CANCELLED: {
- mAmInternal.notifyAppTransitionCancelled();
+ mAtmInternal.notifyAppTransitionCancelled();
}
break;
case NOTIFY_APP_TRANSITION_FINISHED: {
- mAmInternal.notifyAppTransitionFinished();
+ mAtmInternal.notifyAppTransitionFinished();
}
break;
case WINDOW_HIDE_TIMEOUT: {
@@ -5019,7 +5023,7 @@
}
break;
case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
- mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+ mAtmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
}
break;
case RESTORE_POINTER_ICON: {
@@ -5038,11 +5042,11 @@
}
break;
case NOTIFY_KEYGUARD_FLAGS_CHANGED: {
- mAmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj);
+ mAtmInternal.notifyKeyguardFlagsChanged((Runnable) msg.obj);
}
break;
case NOTIFY_KEYGUARD_TRUSTED_CHANGED: {
- mAmInternal.notifyKeyguardTrustedChanged();
+ mAtmInternal.notifyKeyguardTrustedChanged();
}
break;
case SET_HAS_OVERLAY_UI: {
@@ -5712,6 +5716,8 @@
displayContent.setLayoutNeeded();
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
displayContent.performLayout(true /*initial*/, updateInputWindows);
+ } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
+ mRoot.performSurfacePlacement(false);
}
}
@@ -5967,7 +5973,7 @@
@Override
public void setRecentsVisibility(boolean visible) {
- mAmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
"setRecentsVisibility()");
synchronized (mWindowMap) {
mPolicy.setRecentsVisibilityLw(visible);
@@ -5989,7 +5995,7 @@
@Override
public void setShelfHeight(boolean visible, int shelfHeight) {
- mAmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
"setShelfHeight()");
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getPinnedStackController().setAdjustedForShelf(visible,
@@ -7357,16 +7363,18 @@
@Override
public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
- @Nullable IBinder targetWindowToken) {
+ boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
+ mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
+ }
+
+ @Override
+ public void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+ @NonNull IBinder imeTargetWindowToken) {
// TODO (b/34628091): Use this method to address the window animation issue.
if (DEBUG_INPUT_METHOD) {
- Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
- + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed
- + " imeWindowVisible=" + imeWindowVisible
- + " targetWindowToken=" + targetWindowToken);
+ Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
+ + " imeTargetWindowToken=" + imeTargetWindowToken);
}
- mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cbe30cf..c8c4b58 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -100,6 +100,7 @@
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
import static com.android.server.wm.WindowManagerService.localLOGV;
@@ -151,6 +152,8 @@
import static com.android.server.wm.WindowStateProto.VISIBLE_FRAME;
import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
+import static com.android.server.wm.utils.CoordinateTransforms.transformRect;
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
import android.annotation.CallSuper;
import android.app.AppOpsManager;
@@ -1812,7 +1815,8 @@
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& !isDragResizing() && !adjustedForMinimizedDockOrIme
&& getWindowConfiguration().hasMovementAnimations()
- && !mWinAnimator.mLastHidden) {
+ && !mWinAnimator.mLastHidden
+ && !mSeamlesslyRotated) {
startMoveAnimation(left, top);
}
@@ -2061,7 +2065,10 @@
if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+ mService.updateFocusedWindowLocked(mService.mCurrentFocus == this
+ ? UPDATE_FOCUS_REMOVING_FOCUS
+ : UPDATE_FOCUS_NORMAL,
+ true /*updateInputWindows*/);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -4856,6 +4863,29 @@
mFrameNumber = frameNumber;
}
+ @Override
+ void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
+ if (!isVisibleNow() || mIsWallpaper) {
+ return;
+ }
+ final Matrix transform = mTmpMatrix;
+
+ mService.markForSeamlessRotation(this, true);
+
+ // We rotated the screen, but have not performed a new layout pass yet. In the mean time,
+ // we recompute the coordinates of mFrame in the new orientation, so the surface can be
+ // properly placed.
+ transformToRotation(oldRotation, newRotation, getDisplayInfo(), transform);
+ transformRect(transform, mFrame, null /* tmpRectF */);
+
+ updateSurfacePosition(t);
+ mWinAnimator.seamlesslyRotate(t, oldRotation, newRotation);
+
+ // Dispatch to children only after mFrame has been updated, as it's needed in the
+ // child's updateSurfacePosition.
+ super.seamlesslyRotate(t, oldRotation, newRotation);
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 561c9de..14e0e13 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -41,18 +41,18 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
import android.os.Trace;
@@ -366,7 +366,8 @@
mDrawState = READY_TO_SHOW;
boolean result = false;
final AppWindowToken atoken = mWin.mAppToken;
- if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ if (atoken == null || atoken.canShowWindows()
+ || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
@@ -1497,40 +1498,14 @@
}
}
- void seamlesslyRotateWindow(SurfaceControl.Transaction t,
- int oldRotation, int newRotation) {
+ void seamlesslyRotate(SurfaceControl.Transaction t, int oldRotation, int newRotation) {
final WindowState w = mWin;
- if (!w.isVisibleNow() || w.mIsWallpaper) {
- return;
- }
- final Rect cropRect = mService.mTmpRect;
- final Rect displayRect = mService.mTmpRect2;
- final RectF frameRect = mService.mTmpRectF;
+ // We rotated the screen, but have not received a new buffer with the correct size yet. In
+ // the mean time, we rotate the buffer we have to the new orientation.
final Matrix transform = mService.mTmpTransform;
-
- final float x = w.mFrame.left;
- final float y = w.mFrame.top;
- final float width = w.mFrame.width();
- final float height = w.mFrame.height();
-
- mService.getDefaultDisplayContentLocked().getBounds(displayRect);
- final float displayWidth = displayRect.width();
- final float displayHeight = displayRect.height();
-
- // Compute a transform matrix to undo the coordinate space transformation,
- // and present the window at the same physical position it previously occupied.
- final int deltaRotation = DisplayContent.deltaRotation(newRotation, oldRotation);
- DisplayContent.createRotationMatrix(deltaRotation, x, y, displayWidth, displayHeight,
+ transformToRotation(oldRotation, newRotation, w.mFrame.width(), w.mFrame.height(),
transform);
-
- // We just need to apply a rotation matrix to the window. For example
- // if we have a portrait window and rotate to landscape, the window is still portrait
- // and now extends off the bottom of the screen (and only halfway across). Essentially we
- // apply a transform to display the current buffer at it's old position
- // (in the new coordinate space). We then freeze layer updates until the resize
- // occurs, at which point we undo, them.
- mService.markForSeamlessRotation(w, true);
transform.getValues(mService.mTmpFloats);
float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d247960..aced8e4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -16,18 +16,10 @@
package com.android.server.wm;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
-
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
@@ -39,12 +31,18 @@
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
-import static com.android.server.wm.AppTransition.isTaskTransit;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
diff --git a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
index 09d7b5d..a2f37a5 100644
--- a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
+++ b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
@@ -22,7 +22,11 @@
import static android.view.Surface.ROTATION_90;
import android.annotation.Dimension;
+import android.annotation.Nullable;
import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.DisplayInfo;
import android.view.Surface.Rotation;
public class CoordinateTransforms {
@@ -59,4 +63,93 @@
throw new IllegalArgumentException("Unknown rotation: " + rotation);
}
}
+
+ /**
+ * Sets a matrix such that given a rotation, it transforms that rotation's logical coordinates
+ * to physical coordinates.
+ *
+ * @param rotation the rotation to which the matrix should transform
+ * @param out the matrix to be set
+ */
+ public static void transformLogicalToPhysicalCoordinates(@Rotation int rotation,
+ @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+ switch (rotation) {
+ case ROTATION_0:
+ out.reset();
+ break;
+ case ROTATION_90:
+ out.setRotate(90);
+ out.preTranslate(0, -physicalWidth);
+ break;
+ case ROTATION_180:
+ out.setRotate(180);
+ out.preTranslate(-physicalWidth, -physicalHeight);
+ break;
+ case ROTATION_270:
+ out.setRotate(270);
+ out.preTranslate(-physicalHeight, 0);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown rotation: " + rotation);
+ }
+ }
+
+ /**
+ * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
+ * old rotation to coordinates that refer to the same physical location in the new rotation.
+ *
+ * @param oldRotation the rotation to transform from
+ * @param newRotation the rotation to transform to
+ * @param info the display info
+ * @param out a matrix that will be set to the transform
+ */
+ public static void transformToRotation(@Rotation int oldRotation,
+ @Rotation int newRotation, DisplayInfo info, Matrix out) {
+ final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
+ final int h = flipped ? info.logicalWidth : info.logicalHeight;
+ final int w = flipped ? info.logicalHeight : info.logicalWidth;
+
+ final Matrix tmp = new Matrix();
+ transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
+ transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
+ out.postConcat(tmp);
+ }
+
+ /**
+ * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
+ * old rotation to coordinates that refer to the same physical location in the new rotation.
+ *
+ * @param oldRotation the rotation to transform from
+ * @param newRotation the rotation to transform to
+ * @param newWidth the width of the area to transform, in the new rotation
+ * @param newHeight the height of the area to transform, in the new rotation
+ * @param out a matrix that will be set to the transform
+ */
+ public static void transformToRotation(@Rotation int oldRotation,
+ @Rotation int newRotation, int newWidth, int newHeight, Matrix out) {
+ final boolean flipped = newRotation == ROTATION_90 || newRotation == ROTATION_270;
+ final int h = flipped ? newWidth : newHeight;
+ final int w = flipped ? newHeight : newWidth;
+
+ final Matrix tmp = new Matrix();
+ transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
+ transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
+ out.postConcat(tmp);
+ }
+
+ /**
+ * Transforms a rect using a transformation matrix
+ *
+ * @param transform the transformation to apply to the rect
+ * @param inOutRect the rect to transform
+ * @param tmp a temporary value, if null the function will allocate its own.
+ */
+ public static void transformRect(Matrix transform, Rect inOutRect, @Nullable RectF tmp) {
+ if (tmp == null) {
+ tmp = new RectF();
+ }
+ tmp.set(inOutRect);
+ transform.mapRect(tmp);
+ inOutRect.set((int) tmp.left, (int) tmp.top, (int) tmp.right, (int) tmp.bottom);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1c9782f..84de6b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -152,6 +152,11 @@
}
@Override
+ public long forceNetworkLogs() {
+ return 0;
+ }
+
+ @Override
public long forceSecurityLogs() {
return 0;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 414cf47..9ef806e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6259,7 +6259,7 @@
}
@Override
- public void reportFailedFingerprintAttempt(int userHandle) {
+ public void reportFailedBiometricAttempt(int userHandle) {
enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -6270,7 +6270,7 @@
}
@Override
- public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ public void reportSuccessfulBiometricAttempt(int userHandle) {
enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -10694,7 +10694,6 @@
return false;
}
}
-
return false;
}
@@ -12282,6 +12281,20 @@
}
}
+ @Override
+ public long forceNetworkLogs() {
+ enforceShell("forceNetworkLogs");
+ synchronized (getLockObject()) {
+ if (!isNetworkLoggingEnabledInternalLocked()) {
+ throw new IllegalStateException("logging is not available");
+ }
+ if (mNetworkLogger != null) {
+ return mNetworkLogger.forceBatchFinalization();
+ }
+ return 0;
+ }
+ }
+
/** Pauses security and network logging if there are unaffiliated users on the device */
private void maybePauseDeviceWideLoggingLocked() {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0967652..4514492 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -187,4 +187,8 @@
List<NetworkEvent> retrieveLogs(long batchToken) {
return mNetworkLoggingHandler.retrieveFullLogBatch(batchToken);
}
+
+ long forceBatchFinalization() {
+ return mNetworkLoggingHandler.forceBatchFinalization();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index f91f959..0a7070f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DeviceAdminReceiver;
@@ -33,6 +35,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* A Handler class for managing network logging on a background thread.
@@ -60,6 +63,12 @@
/** Delay after which older batches get discarded after a retrieval. */
private static final long RETRIEVED_BATCH_DISCARD_DELAY_MS = 5 * 60 * 1000; // 5m
+ /** Throttle batch finalization to 10 seconds.*/
+ private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10);
+ /** Timestamp of the last call to finalise a batch. Used for throttling forced finalization.*/
+ @GuardedBy("this")
+ private long mLastFinalizationNanos = -1;
+
/** Do not call into mDpm with locks held */
private final DevicePolicyManagerService mDpm;
private final AlarmManager mAlarmManager;
@@ -155,6 +164,26 @@
+ "ms from now.");
}
+ /**
+ * Forces batch finalisation. Throttled to 10 seconds per batch finalisation.
+ * @return the number of milliseconds to wait until batch finalisation can be forced.
+ */
+ long forceBatchFinalization() {
+ Bundle notificationExtras;
+ synchronized (this) {
+ final long toWaitNanos =
+ mLastFinalizationNanos + FORCE_FETCH_THROTTLE_NS - System.nanoTime();
+ if (toWaitNanos > 0) {
+ return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
+ }
+ notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+ }
+ if (notificationExtras != null) {
+ notifyDeviceOwner(notificationExtras);
+ }
+ return 0;
+ }
+
synchronized void pause() {
Slog.d(TAG, "Paused network logging");
mPaused = true;
@@ -192,6 +221,7 @@
@GuardedBy("this")
/** @returns extras if a message should be sent to the device owner */
private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() {
+ mLastFinalizationNanos = System.nanoTime();
Bundle notificationExtras = null;
if (mNetworkEvents.size() > 0) {
// Assign ids to the events.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2c0120b..4456ed0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -75,7 +75,8 @@
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.emergency.EmergencyAffordanceService;
-import com.android.server.fingerprint.FingerprintService;
+import com.android.server.biometrics.face.FaceService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
import com.android.server.job.JobSchedulerService;
@@ -559,14 +560,13 @@
// Activity manager runs the show.
traceBeginAndSlog("StartActivityManager");
- mActivityManagerService = mSystemServiceManager.startService(
- ActivityManagerService.Lifecycle.class).getService();
- mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
- mActivityManagerService.setInstaller(installer);
// TODO: Might need to move after migration to WM.
ActivityTaskManagerService atm = mSystemServiceManager.startService(
ActivityTaskManagerService.Lifecycle.class).getService();
- mActivityManagerService.setActivityTaskManager(atm);
+ mActivityManagerService = ActivityManagerService.Lifecycle.startService(
+ mSystemServiceManager, atm);
+ mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
+ mActivityManagerService.setInstaller(installer);
traceEnd();
// Power manager needs to be started early because other services need it.
@@ -1513,6 +1513,12 @@
}
traceEnd();
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ traceBeginAndSlog("StartFaceSensor");
+ mSystemServiceManager.startService(FaceService.class);
+ traceEnd();
+ }
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
traceBeginAndSlog("StartFingerprintSensor");
mSystemServiceManager.startService(FingerprintService.class);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
index f741c70..583a9dd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
@@ -50,7 +50,7 @@
@RunWith(AndroidJUnit4.class)
public class ActivityLaunchParamsModifierTests extends ActivityTestsBase {
private ActivityLaunchParamsModifier mModifier;
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -62,7 +62,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mModifier = new ActivityLaunchParamsModifier(mService.mStackSupervisor);
mCurrent = new LaunchParams();
mResult = new LaunchParams();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 5ee1c40..dd3e5a8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -73,7 +73,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private TestActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -83,7 +83,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
@@ -126,7 +126,7 @@
pauseFound.value = true;
}
return null;
- }).when(mActivity.app.thread).scheduleTransaction(any());
+ }).when(mActivity.app.getThread()).scheduleTransaction(any());
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1ce41a6..a44661d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -68,7 +68,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityStack mFullscreenStack;
@@ -77,7 +77,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -174,7 +174,7 @@
// #notifyAll will be called on the ActivityManagerService. we must hold the object lock
// when this happens.
- synchronized (mSupervisor.mService) {
+ synchronized (mSupervisor.mService.mGlobalLock) {
final WaitResult taskToFrontWait = new WaitResult();
mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
@@ -278,7 +278,7 @@
assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
// Let's pretend that the app has crashed.
- firstActivity.app.thread = null;
+ firstActivity.app.setThread(null);
mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
// Verify that the stack was removed.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 01425ed..0d0a6ba 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -61,7 +61,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityDisplay mDefaultDisplay;
private ActivityStack mStack;
@@ -72,7 +72,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
mStack = mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index 7948e4c..f5ae46cd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,7 +19,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import android.app.IApplicationThread;
import android.content.Intent;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -49,7 +51,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStartControllerTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStartController mController;
private Factory mFactory;
private ActivityStarter mStarter;
@@ -57,11 +59,11 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mFactory = mock(Factory.class);
mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
- mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor,
- mock(ActivityStartInterceptor.class)));
+ mStarter = spy(new ActivityStarter(mController, mService,
+ mService.mStackSupervisor, mock(ActivityStartInterceptor.class)));
doReturn(mStarter).when(mFactory).obtain();
}
@@ -77,11 +79,14 @@
final int startFlags = random.nextInt();
final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ProcessRecord process= new ProcessRecord(null, null,
- mService.mContext.getApplicationInfo(), "name", 12345);
+ final WindowProcessController wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), "name", 12345,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
mController.addPendingActivityLaunch(
- new PendingActivityLaunch(activity, source, startFlags, stack, process));
+ new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
final boolean resume = random.nextBoolean();
mController.doPendingActivityLaunches(resume);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 9d35ef1..7f55824 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -57,7 +57,7 @@
* Unit tests for {@link ActivityStartInterceptorTest}.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
+ * atest FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
*/
@Presubmit
@SmallTest
@@ -82,7 +82,9 @@
@Mock
private Context mContext;
@Mock
- private ActivityManagerService mService;
+ private ActivityManagerService mAm;
+ @Mock
+ private ActivityTaskManagerService mService;
@Mock
private ActivityStackSupervisor mSupervisor;
@Mock
@@ -104,6 +106,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mService.mAm = mAm;
mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext,
mUserController);
mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
@@ -113,10 +116,9 @@
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
LocalServices.addService(DevicePolicyManagerInternal.class,
mDevicePolicyManager);
- when(mDevicePolicyManager
- .createShowAdminSupportIntent(TEST_USER_ID, true))
+ when(mDevicePolicyManager.createShowAdminSupportIntent(TEST_USER_ID, true))
.thenReturn(ADMIN_SUPPORT_INTENT);
- when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
+ when(mAm.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
// Mock UserManager
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -129,7 +131,7 @@
thenReturn(CONFIRM_CREDENTIALS_INTENT);
// Mock PackageManager
- when(mService.getPackageManager()).thenReturn(mPackageManager);
+ when(mAm.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(null);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 74e5816..267e689 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -88,7 +88,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStarterTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStarter mStarter;
private ActivityStartController mController;
@@ -107,7 +107,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mController = mock(ActivityStartController.class);
mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
mock(ActivityStartInterceptor.class));
@@ -134,7 +134,7 @@
assertTrue(task2.getStack() instanceof PinnedActivityStack);
mStarter.updateBounds(task2, bounds);
- verify(mService.mActivityTaskManager, times(1)).resizeStack(eq(task2.getStack().mStackId),
+ verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
// In the case of no animation, the stack and task bounds should be set immediately.
@@ -189,20 +189,21 @@
*/
private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
int expectedResult) {
- final ActivityManagerService service = createActivityManagerService();
+ final ActivityTaskManagerService service = mService;
final IPackageManager packageManager = mock(IPackageManager.class);
final ActivityStartController controller = mock(ActivityStartController.class);
final ActivityStarter starter = new ActivityStarter(controller, service,
service.mStackSupervisor, mock(ActivityStartInterceptor.class));
+ prepareStarter(launchFlags);
final IApplicationThread caller = mock(IApplicationThread.class);
// If no caller app, return {@code null} {@link ProcessRecord}.
final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new ProcessRecord(null, mock(BatteryStatsImpl.class),
+ ? null : new ProcessRecord(service.mAm, mock(BatteryStatsImpl.class),
mock(ApplicationInfo.class), null, 0);
- doReturn(record).when(service).getRecordForAppLocked(anyObject());
+ doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
final Intent intent = new Intent();
intent.setFlags(launchFlags);
@@ -236,8 +237,8 @@
}
if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
- doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
- anyInt(), any());
+ doReturn(false).when(service).checkAppSwitchAllowedLocked(
+ anyInt(), anyInt(), anyInt(), anyInt(), any());
}
if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 0154d36..1dd7b4c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -29,11 +29,14 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import com.android.server.wm.DisplayWindowController;
import org.junit.Rule;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import android.app.IApplicationThread;
@@ -48,6 +51,7 @@
import android.hardware.display.DisplayManager;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.support.test.InstrumentationRegistry;
import android.testing.DexmakerShareClassLoaderRule;
@@ -101,25 +105,33 @@
mHandlerThread.quitSafely();
}
- protected ActivityManagerService createActivityManagerService() {
- final ActivityManagerService service =
- setupActivityManagerService(new TestActivityManagerService(mContext));
- AttributeCache.init(mContext);
- return service;
+ protected ActivityTaskManagerService createActivityTaskManagerService() {
+ final TestActivityTaskManagerService atm = spy(new TestActivityTaskManagerService(mContext));
+ setupActivityManagerService(atm);
+ return atm;
}
- protected ActivityManagerService setupActivityManagerService(ActivityManagerService service) {
- service = spy(service);
+ protected ActivityManagerService createActivityManagerService() {
+ final TestActivityTaskManagerService atm = spy(new TestActivityTaskManagerService(mContext));
+ return setupActivityManagerService(atm);
+ }
+
+ ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
+ final ActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+ setupActivityManagerService(am, atm);
+ AttributeCache.init(mContext);
+ return am;
+ }
+
+ void setupActivityManagerService(ActivityManagerService am, ActivityTaskManagerService atm) {
+ atm.setActivityManagerService(am);
+ atm.mAmInternal = am.new LocalService();
// Makes sure the supervisor is using with the spy object.
- service.mStackSupervisor.setService(service);
- // Makes sure activity task is created with the spy object.
- TestActivityTaskManagerService atm =
- spy(new TestActivityTaskManagerService(service.mContext));
- service.setActivityTaskManager(atm);
- doReturn(mock(IPackageManager.class)).when(service).getPackageManager();
- doNothing().when(service).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- service.mWindowManager = prepareMockWindowManager();
- return service;
+ atm.mStackSupervisor.setService(atm);
+ doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+ doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+ am.mWindowManager = prepareMockWindowManager();
+ atm.setWindowManager(am.mWindowManager);
}
/**
@@ -131,7 +143,7 @@
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private ComponentName mComponent;
private TaskRecord mTaskRecord;
@@ -140,7 +152,7 @@
private ActivityStack mStack;
private int mActivityFlags;
- ActivityBuilder(ActivityManagerService service) {
+ ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
}
@@ -211,10 +223,12 @@
mTaskRecord.addActivityToTop(activity);
}
- activity.setProcess(new ProcessRecord(null, null,
- mService.mContext.getApplicationInfo(), "name", 12345));
- activity.app.thread = mock(IApplicationThread.class);
-
+ final WindowProcessController wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), "name", 12345,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
+ activity.setProcess(wpc);
return activity;
}
}
@@ -322,7 +336,7 @@
}
private static class TestTaskRecord extends TaskRecord {
- TestTaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info,
+ TestTaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info,
Intent _intent, IVoiceInteractionSession _voiceSession,
IVoiceInteractor _voiceInteractor) {
super(service, _taskId, info, _intent, _voiceSession, _voiceInteractor);
@@ -340,37 +354,23 @@
}
protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
- TestActivityTaskManagerService(Context context) {
- super(context);
- }
- }
-
- /**
- * An {@link ActivityManagerService} subclass which provides a test
- * {@link ActivityStackSupervisor}.
- */
- protected static class TestActivityManagerService extends ActivityManagerService {
- private ClientLifecycleManager mLifecycleManager;
private LockTaskController mLockTaskController;
- TestActivityManagerService(Context context) {
+ TestActivityTaskManagerService(Context context) {
super(context);
mSupportsMultiWindow = true;
mSupportsMultiDisplay = true;
mSupportsSplitScreenMultiWindow = true;
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
- mWindowManager = WindowTestUtils.getMockWindowManagerService();
}
@Override
- public ClientLifecycleManager getLifecycleManager() {
- if (mLifecycleManager == null) {
- return super.getLifecycleManager();
- }
- return mLifecycleManager;
+ int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+ return userId;
}
+ @Override
public LockTaskController getLockTaskController() {
if (mLockTaskController == null) {
mLockTaskController = spy(super.getLockTaskController());
@@ -405,12 +405,28 @@
}
protected ActivityStackSupervisor createTestSupervisor() {
- return new TestActivityStackSupervisor(this, mHandlerThread.getLooper());
+ return new TestActivityStackSupervisor(this, mH.getLooper());
+ }
+ }
+
+ /**
+ * An {@link ActivityManagerService} subclass which provides a test
+ * {@link ActivityStackSupervisor}.
+ */
+ protected static class TestActivityManagerService extends ActivityManagerService {
+
+ TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
+ super(context, atm);
}
@Override
void updateUsageStats(ActivityRecord component, boolean resumed) {
}
+
+ @Override
+ Configuration getGlobalConfiguration() {
+ return mContext.getResources().getConfiguration();
+ }
}
/**
@@ -421,7 +437,7 @@
private ActivityDisplay mDisplay;
private KeyguardController mKeyguardController;
- public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+ public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
super(service, looper);
mDisplayManager =
(DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index 0a436b9..f74d116 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -113,7 +113,7 @@
mHandler = new Handler(Looper.getMainLooper());
mCallbacksLock = new Object();
mCallbacks = new Callbacks();
- mDataRequester = new AssistDataRequester(mContext, mAm, mWm, mAppOpsManager, mCallbacks,
+ mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
// Gate the continuation of the assist data callbacks until we are ready within the tests
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
index 93e0b5a..fbe552d 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -57,14 +57,14 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class LaunchParamsControllerTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private LaunchParamsController mController;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mController = new LaunchParamsController(mService);
}
@@ -200,9 +200,9 @@
mController.registerModifier(positioner);
- doNothing().when(mService.mActivityTaskManager).moveStackToDisplay(anyInt(), anyInt());
+ doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
mController.layoutTask(task, null /* windowLayout */);
- verify(mService.mActivityTaskManager, times(1)).moveStackToDisplay(eq(task.getStackId()),
+ verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
eq(params.mPreferredDisplayId));
}
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
index 2baf995..e73661b 100644
--- a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
@@ -62,7 +62,7 @@
mService.mHandlerThread.getThreadHandler().runWithScissors(() -> {
mHandler = new TestHandler(null, mClock);
}, 0);
- mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+ mRegistry = new PendingRemoteAnimationRegistry(mService.mActivityTaskManager, mHandler);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index af6686f..a4e4409 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -94,7 +94,7 @@
private static int INVALID_STACK_ID = 999;
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityManagerService mService;
+ private TestActivityTaskManagerService mService;
private ActivityDisplay mDisplay;
private ActivityDisplay mOtherDisplay;
private ActivityStack mStack;
@@ -145,7 +145,9 @@
super.setUp();
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
+ mService = spy(new MyTestActivityTaskManagerService(mContext));
+ final ActivityManagerService am = spy(new MyTestActivityManagerService(mContext, mService));
+ setupActivityManagerService(am, mService);
mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
mHomeStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
@@ -673,9 +675,8 @@
@Test
public void testNotRecentsComponent_denyApiAccess() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
- anyInt(), anyInt());
-
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
+ .checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Expect the following methods to fail due to recents component not being set
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
testRecentTasksApis(false /* expectNoSecurityException */);
@@ -686,8 +687,8 @@
@Test
public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
- anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
+ .checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Set the recents component and ensure that the following calls do not fail
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
@@ -696,58 +697,52 @@
}
private void testRecentTasksApis(boolean expectCallable) {
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.removeStack(INVALID_STACK_ID));
+ assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.removeStacksInWindowingModes(
- new int[] {WINDOWING_MODE_UNDEFINED}));
+ () -> mService.removeStacksInWindowingModes(new int[] {WINDOWING_MODE_UNDEFINED}));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.removeStacksWithActivityTypes(
- new int[] {ACTIVITY_TYPE_UNDEFINED}));
+ () -> mService.removeStacksWithActivityTypes(new int[] {ACTIVITY_TYPE_UNDEFINED}));
assertSecurityException(expectCallable, () -> mService.removeTask(0));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.setTaskWindowingMode(
- 0, WINDOWING_MODE_UNDEFINED, true));
+ () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
assertSecurityException(expectCallable,
() -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.setTaskWindowingModeSplitScreenPrimary(0,
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.dismissSplitScreenMode(true));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.dismissPip(true, 0));
+ assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+ assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+ () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
assertSecurityException(expectCallable,
() -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+ () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
new Rect()));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.resizePinnedStack(new Rect(), new Rect()));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.getAllStackInfos());
+ () -> mService.resizePinnedStack(new Rect(), new Rect()));
+ assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+ () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
assertSecurityException(expectCallable, () -> {
try {
- mService.mActivityTaskManager.getFocusedStackInfo();
+ mService.getFocusedStackInfo();
} catch (RemoteException e) {
// Ignore
}
});
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+ () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.startActivityFromRecents(0, new Bundle()));
- assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.getTaskSnapshot(0, true));
- assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.registerTaskStackListener(null));
- assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.unregisterTaskStackListener(null));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.getTaskDescription(0));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.cancelTaskWindowTransition(0));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.startRecentsActivity(null, null,
+ () -> mService.startActivityFromRecents(0, new Bundle()));
+ assertSecurityException(expectCallable,() -> mService.getTaskSnapshot(0, true));
+ assertSecurityException(expectCallable,() -> mService.registerTaskStackListener(null));
+ assertSecurityException(expectCallable,() -> mService.unregisterTaskStackListener(null));
+ assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+ assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+ assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
null));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.cancelRecentsAnimation(true));
+ assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
}
@@ -827,19 +822,26 @@
}
}
- private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService(Context context) {
+ private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
+ MyTestActivityTaskManagerService(Context context) {
super(context);
}
@Override
- protected ActivityStackSupervisor createTestSupervisor() {
- return new MyTestActivityStackSupervisor(this, mHandlerThread.getLooper());
+ protected RecentTasks createRecentTasks() {
+ return new TestRecentTasks(this, mTaskPersister, new TestUserController(mAm));
}
@Override
- protected RecentTasks createRecentTasks() {
- return new TestRecentTasks(this, mTaskPersister, new TestUserController(this));
+ protected ActivityStackSupervisor createTestSupervisor() {
+ return new MyTestActivityStackSupervisor(this, mH.getLooper());
+ }
+
+ }
+
+ private class MyTestActivityManagerService extends TestActivityManagerService {
+ MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
+ super(context, atm);
}
@Override
@@ -849,7 +851,7 @@
}
private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
- public MyTestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
+ public MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
super(service, looper);
}
@@ -952,7 +954,7 @@
boolean lastAllowed;
- TestRecentTasks(ActivityManagerService service, TaskPersister taskPersister,
+ TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister,
UserController userController) {
super(service, taskPersister, userController);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
index eefd973..b642d26 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
@@ -16,18 +16,16 @@
package com.android.server.am;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -51,10 +49,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class RecentsAnimationTest extends ActivityTestsBase {
- private static final int TEST_CALLING_PID = 3;
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityManagerService mService;
+ private TestActivityTaskManagerService mService;
private ComponentName mRecentsComponent;
@Before
@@ -63,8 +60,8 @@
super.setUp();
mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
- mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
- AttributeCache.init(mContext);
+ mService = spy(new MyTestActivityTaskManagerService(mContext));
+ setupActivityManagerService(mService);
}
@Test
@@ -99,8 +96,8 @@
eq(REORDER_KEEP_IN_PLACE), any());
}
- private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService(Context context) {
+ private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
+ MyTestActivityTaskManagerService(Context context) {
super(context);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index c6ce7e1..944f20f 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -50,7 +50,7 @@
public class RunningTasksTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private RunningTasks mRunningTasks;
@@ -59,7 +59,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mRunningTasks = new RunningTasks();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 3d323f0..f71a6e7 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -57,7 +57,7 @@
private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT);
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStack mStack;
private TaskRecord mTask;
@@ -71,7 +71,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mStack.requestResize(STACK_BOUNDS);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 057fdc8..72851d0 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -155,7 +155,8 @@
}
private TaskRecord createTaskRecord(int taskId) {
- return new TaskRecord(mService, taskId, new Intent(), null, null, null, null, null, false,
+ return new TaskRecord(mService.mActivityTaskManager, taskId, new Intent(), null, null, null,
+ null, null, false,
false, false, 0, 10050, null, new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0,
null, 0, false, false, false, 0, 0);
}
@@ -164,7 +165,7 @@
private boolean mCreated = false;
@Override
- TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+ TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
mCreated = true;
@@ -172,7 +173,7 @@
}
@Override
- TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+ TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent,
ActivityManager.TaskDescription taskDescription) {
mCreated = true;
@@ -180,7 +181,7 @@
}
@Override
- TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+ TaskRecord create(ActivityTaskManagerService service, int taskId, Intent intent,
Intent affinityIntent, String affinity, String rootAffinity,
ComponentName realActivity,
ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 998ffa0..2de5d87 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -92,6 +92,7 @@
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -613,6 +614,7 @@
protected UserManagerInternal mMockUserManagerInternal;
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
protected ActivityManagerInternal mMockActivityManagerInternal;
+ protected ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
protected static final int CALLING_UID_1 = 10001;
@@ -752,6 +754,7 @@
mMockUserManagerInternal = mock(UserManagerInternal.class);
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
+ mMockActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
@@ -759,6 +762,7 @@
LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
@@ -1641,11 +1645,11 @@
protected Intent[] launchShortcutAndGetIntentsInner(Runnable shortcutStarter,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
- reset(mMockActivityManagerInternal);
+ reset(mMockActivityTaskManagerInternal);
shortcutStarter.run();
final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
- verify(mMockActivityManagerInternal).startActivitiesAsPackage(
+ verify(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
eq(packageName),
eq(userId),
intentsCaptor.capture(),
@@ -1695,7 +1699,7 @@
protected void assertShortcutNotLaunched(@NonNull String packageName,
@NonNull String shortcutId, int userId) {
- reset(mMockActivityManagerInternal);
+ reset(mMockActivityTaskManagerInternal);
try {
mLauncherApps.startShortcut(packageName, shortcutId, null, null,
UserHandle.of(userId));
@@ -1703,7 +1707,7 @@
} catch (ActivityNotFoundException expected) {
}
// This shouldn't have been called.
- verify(mMockActivityManagerInternal, times(0)).startActivitiesAsPackage(
+ verify(mMockActivityTaskManagerInternal, times(0)).startActivitiesAsPackage(
anyString(),
anyInt(),
any(Intent[].class),
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index 33acc44..c4c2ad9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -31,6 +31,8 @@
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,6 +74,8 @@
private AppOpsManager mAppOpsManager;
@Mock
private ActivityManagerInternal mActivityManagerInternal;
+ @Mock
+ private ActivityTaskManagerInternal mActivityTaskManagerInternal;
private TestInjector mTestInjector;
private ActivityInfo mActivityInfo;
@@ -210,7 +214,7 @@
ACTIVITY_COMPONENT,
UserHandle.of(PRIMARY_USER)));
- verify(mActivityManagerInternal, never())
+ verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
nullable(IApplicationThread.class),
anyString(),
@@ -232,7 +236,7 @@
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
- verify(mActivityManagerInternal, never())
+ verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
nullable(IApplicationThread.class),
anyString(),
@@ -252,7 +256,7 @@
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
- verify(mActivityManagerInternal, never())
+ verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
nullable(IApplicationThread.class),
anyString(),
@@ -274,7 +278,7 @@
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
- verify(mActivityManagerInternal, never())
+ verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
nullable(IApplicationThread.class),
anyString(),
@@ -294,7 +298,7 @@
new ComponentName(PACKAGE_TWO, "test"),
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
- verify(mActivityManagerInternal, never())
+ verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
nullable(IApplicationThread.class),
anyString(),
@@ -314,7 +318,7 @@
ACTIVITY_COMPONENT,
UserHandle.of(SECONDARY_USER)));
- verify(mActivityManagerInternal, never())
+ verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
nullable(IApplicationThread.class),
anyString(),
@@ -333,7 +337,7 @@
ACTIVITY_COMPONENT,
UserHandle.of(PRIMARY_USER));
- verify(mActivityManagerInternal)
+ verify(mActivityTaskManagerInternal)
.startActivityAsUser(
nullable(IApplicationThread.class),
eq(PACKAGE_ONE),
@@ -432,5 +436,10 @@
public ActivityManagerInternal getActivityManagerInternal() {
return mActivityManagerInternal;
}
+
+ @Override
+ public ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+ return mActivityTaskManagerInternal;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 7815004..6c6c9932 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -2904,7 +2904,7 @@
runWithCaller(LAUNCHER_1, USER_0, () -> {
// Not launchable.
doReturn(ActivityManager.START_CLASS_NOT_FOUND)
- .when(mMockActivityManagerInternal).startActivitiesAsPackage(
+ .when(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
anyStringOrNull(), anyInt(),
anyOrNull(Intent[].class), anyOrNull(Bundle.class));
assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
@@ -2912,7 +2912,7 @@
// Still not launchable.
doReturn(ActivityManager.START_CLASS_NOT_FOUND)
- .when(mMockActivityManagerInternal)
+ .when(mMockActivityTaskManagerInternal)
.startActivitiesAsPackage(
anyStringOrNull(), anyInt(),
anyOrNull(Intent[].class), anyOrNull(Bundle.class));
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index ac196f9..bb9b1c4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -380,21 +380,39 @@
}
/**
- * This test enforces that the pinned stack is always kept as the top stack.
+ * This test enforces that alwaysOnTop stack is placed at proper position.
*/
@Test
- public void testPinnedStackLocation() {
+ public void testAlwaysOnTopStackLocation() {
+ final TaskStack alwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(alwaysOnTopStack, 0 /* userId */);
+ alwaysOnTopStack.setAlwaysOnTop(true);
+ mDisplayContent.positionStackAt(POSITION_TOP, alwaysOnTopStack);
+ assertTrue(alwaysOnTopStack.isAlwaysOnTop());
+ // Ensure always on top state is synced to the children of the stack.
+ assertTrue(alwaysOnTopStack.getTopChild().isAlwaysOnTop());
+ assertEquals(alwaysOnTopStack, mDisplayContent.getTopStack());
+
final TaskStack pinnedStack = createStackControllerOnStackOnDisplay(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
- // Ensure that the pinned stack is the top stack
assertEquals(pinnedStack, mDisplayContent.getPinnedStack());
assertEquals(pinnedStack, mDisplayContent.getTopStack());
- // By default, this should try to create a new stack on top
- final TaskStack otherStack = createTaskStackOnDisplay(mDisplayContent);
- // Ensure that the other stack is on the display.
- assertEquals(mDisplayContent, otherStack.getDisplayContent());
- // Ensure that the pinned stack is still on top
- assertEquals(pinnedStack, mDisplayContent.getTopStack());
+
+ final TaskStack anotherAlwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
+ anotherAlwaysOnTopStack.setAlwaysOnTop(true);
+ mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack);
+ assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
+ int topPosition = mDisplayContent.getStacks().size() - 1;
+ // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
+ // existing alwaysOnTop stack.
+ assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));
+
+ final TaskStack nonAlwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
+ assertEquals(mDisplayContent, nonAlwaysOnTopStack.getDisplayContent());
+ topPosition = mDisplayContent.getStacks().size() - 1;
+ // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
+ // existing other non-alwaysOnTop stacks.
+ assertEquals(nonAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 3));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index 513c1ec..10d7aad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -31,6 +31,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP;
import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
@@ -81,6 +82,11 @@
assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE,
winConfig1.diff(winConfig2, false /* compareUndefined */));
+ winConfig2.setAlwaysOnTop(true);
+ assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE
+ | WINDOW_CONFIG_ALWAYS_ON_TOP,
+ winConfig1.diff(winConfig2, false /* compareUndefined */));
+
assertEquals(0, config1.diff(config3));
assertEquals(0, config1.diffPublicOnly(config3));
assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */));
@@ -108,6 +114,12 @@
assertNotEquals(winConfig1.compareTo(winConfig2), 0);
winConfig2.setWindowingMode(winConfig1.getWindowingMode());
+ // Different always on top state
+ winConfig2.setAlwaysOnTop(true);
+ assertNotEquals(config1.compareTo(config2), 0);
+ assertNotEquals(winConfig1.compareTo(winConfig2), 0);
+ winConfig2.setAlwaysOnTop(winConfig1.isAlwaysOnTop());
+
// Different bounds
winConfig2.setAppBounds(0, 2, 3, 4);
assertNotEquals(config1.compareTo(config2), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index d91079e..d13c3c9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -91,15 +91,17 @@
LocalServices.addService(ActivityManagerInternal.class,
mock(ActivityManagerInternal.class));
- final ActivityManagerInternal am =
- LocalServices.getService(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class,
+ mock(ActivityTaskManagerInternal.class));
+ final ActivityTaskManagerInternal atm =
+ LocalServices.getService(ActivityTaskManagerInternal.class);
doAnswer((InvocationOnMock invocationOnMock) -> {
final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
if (runnable != null) {
runnable.run();
}
return null;
- }).when(am).notifyKeyguardFlagsChanged(any());
+ }).when(atm).notifyKeyguardFlagsChanged(any());
InputManagerService ims = mock(InputManagerService.class);
// InputChannel is final and can't be mocked.
@@ -125,6 +127,7 @@
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 85e846d..9f113ad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,6 +31,8 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
+import static android.view.Surface.ROTATION_0;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -48,8 +52,10 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -349,6 +355,32 @@
assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId()));
}
+ @Test
+ public void testSeamlesslyRotateWindow() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+ app.mHasSurface = true;
+ app.mSurfaceControl = mock(SurfaceControl.class);
+ app.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+ try {
+ app.mFrame.set(10, 20, 60, 80);
+
+ app.seamlesslyRotate(t, ROTATION_0, ROTATION_90);
+
+ assertTrue(app.mSeamlesslyRotated);
+ assertEquals(new Rect(20, mDisplayInfo.logicalWidth - 60,
+ 80, mDisplayInfo.logicalWidth - 10), app.mFrame);
+
+ verify(t).setPosition(app.mSurfaceControl, app.mFrame.left, app.mFrame.top);
+ verify(app.mWinAnimator.mSurfaceController).setPosition(t, 0, 50, false);
+ verify(app.mWinAnimator.mSurfaceController).setMatrix(t, 0, -1, 1, 0, false);
+ } finally {
+ app.mSurfaceControl = null;
+ app.mHasSurface = false;
+ }
+ }
+
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
reset(mPowerManagerWrapper);
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 40a10e0..361522c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -21,14 +21,19 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.utils.CoordinateTransforms.transformLogicalToPhysicalCoordinates;
import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
+
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
+import android.view.DisplayInfo;
import org.junit.Before;
import org.junit.Rule;
@@ -41,6 +46,7 @@
private static final int H = 400;
private final Matrix mMatrix = new Matrix();
+ private final Matrix mMatrix2 = new Matrix();
@Rule
public final ErrorCollector mErrorCollector = new ErrorCollector();
@@ -48,39 +54,140 @@
@Before
public void setUp() throws Exception {
mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef);
+ mMatrix2.setTranslate(0xbeefdead, 0xbeefdead);
}
@Test
- public void transformPhysicalToLogicalCoordinates_rot0() throws Exception {
+ public void transformPhysicalToLogicalCoordinates_rot0() {
transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix);
assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
}
@Test
- public void transformPhysicalToLogicalCoordinates_rot90() throws Exception {
+ public void transformPhysicalToLogicalCoordinates_rot90() {
transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix);
- checkDevicePoint(0, 0).mapsToLogicalPoint(0, W);
- checkDevicePoint(W, H).mapsToLogicalPoint(H, 0);
+ checkPoint(0, 0).transformsTo(0, W);
+ checkPoint(W, H).transformsTo(H, 0);
}
@Test
- public void transformPhysicalToLogicalCoordinates_rot180() throws Exception {
+ public void transformPhysicalToLogicalCoordinates_rot180() {
transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix);
- checkDevicePoint(0, 0).mapsToLogicalPoint(W, H);
- checkDevicePoint(W, H).mapsToLogicalPoint(0, 0);
+ checkPoint(0, 0).transformsTo(W, H);
+ checkPoint(W, H).transformsTo(0, 0);
}
@Test
- public void transformPhysicalToLogicalCoordinates_rot270() throws Exception {
+ public void transformPhysicalToLogicalCoordinates_rot270() {
transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix);
- checkDevicePoint(0, 0).mapsToLogicalPoint(H, 0);
- checkDevicePoint(W, H).mapsToLogicalPoint(0, W);
+ checkPoint(0, 0).transformsTo(H, 0);
+ checkPoint(W, H).transformsTo(0, W);
}
- private DevicePointAssertable checkDevicePoint(int x, int y) {
+ @Test
+ public void transformLogicalToPhysicalCoordinates_rot0() {
+ transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
+ assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
+ }
+
+ @Test
+ public void transformLogicalToPhysicalCoordinates_rot90() {
+ transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
+
+ checkPoint(0, W).transformsTo(0, 0);
+ checkPoint(H, 0).transformsTo(W, H);
+}
+
+ @Test
+ public void transformLogicalToPhysicalCoordinates_rot180() {
+ transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
+
+ checkPoint(W, H).transformsTo(0, 0);
+ checkPoint(0, 0).transformsTo(W, H);
+ }
+
+ @Test
+ public void transformLogicalToPhysicalCoordinates_rot270() {
+ transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
+
+ checkPoint(H, 0).transformsTo(0, 0);
+ checkPoint(0, W).transformsTo(W, H);
+ }
+
+ @Test
+ public void transformLogicalToPhysicalCoordinatesIsInverse_rot0() {
+ transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
+ transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix2);
+
+ assertMatricesAreInverses(mMatrix, mMatrix2);
+ }
+
+ @Test
+ public void transformLogicalToPhysicalCoordinatesIsInverse_rot90() {
+ transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
+ transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix2);
+
+ assertMatricesAreInverses(mMatrix, mMatrix2);
+ }
+
+ @Test
+ public void transformLogicalToPhysicalCoordinatesIsInverse_rot180() {
+ transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
+ transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix2);
+
+ assertMatricesAreInverses(mMatrix, mMatrix2);
+ }
+
+ @Test
+ public void transformLogicalToPhysicalCoordinatesIsInverse_rot270() {
+ transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
+ transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix2);
+
+ assertMatricesAreInverses(mMatrix, mMatrix2);
+ }
+
+ @Test
+ public void transformBetweenRotations_rot180_rot270() {
+ // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
+ transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix);
+
+ checkPoint(0, 0).transformsTo(0, W);
+ checkPoint(W, H).transformsTo(H, 0);
+ }
+
+ @Test
+ public void transformBetweenRotations_rot90_rot0() {
+ transformToRotation(ROTATION_180, ROTATION_270, W, H, mMatrix);
+
+ checkPoint(0, 0).transformsTo(0, H);
+ // H,W is bottom right in ROT_90
+ checkPoint(H, W).transformsTo(W, 0);
+ }
+
+ @Test
+ public void transformBetweenRotations_displayInfo() {
+ final DisplayInfo di = new DisplayInfo();
+ di.rotation = ROTATION_90;
+ di.logicalWidth = H; // dimensions are flipped in ROT_90
+ di.logicalHeight = W;
+ transformToRotation(ROTATION_180, ROTATION_270, di, mMatrix);
+
+ // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
+ transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix2);
+
+ assertEquals(mMatrix2, mMatrix);
+ }
+
+ private void assertMatricesAreInverses(Matrix matrix, Matrix matrix2) {
+ final Matrix concat = new Matrix();
+ concat.setConcat(matrix, matrix2);
+ assertTrue("expected identity, but was: " + concat, concat.isIdentity());
+ }
+
+ private TransformPointAssertable checkPoint(int x, int y) {
final Point devicePoint = new Point(x, y);
final float[] fs = new float[] {x, y};
mMatrix.mapPoints(fs);
@@ -92,7 +199,7 @@
};
}
- public interface DevicePointAssertable {
- void mapsToLogicalPoint(int x, int y);
+ public interface TransformPointAssertable {
+ void transformsTo(int x, int y);
}
}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
index 2241047..ca473c6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
@@ -19,20 +19,26 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
import android.app.NotificationChannel;
+import android.net.Uri;
import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@SmallTest
@@ -68,4 +74,23 @@
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
channel.writeXml(serializer);
}
+
+ @Test
+ public void testBackupEmptySound() throws Exception {
+ NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+ channel.setSound(Uri.EMPTY, null);
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ channel.writeXmlForBackup(serializer, getContext());
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+ restored.populateFromXmlForRestore(parser, getContext());
+
+ assertNull(restored.getSound());
+ }
}
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 3b21390..45a3c41 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2405,6 +2405,45 @@
}
@Test
+ public void testTooLateAdjustmentTriggersUpdate() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ waitForIdle();
+
+ verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
+ }
+
+ @Test
+ public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
+
+ assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE,
+ r.getUserSentiment());
+ }
+
+ @Test
public void testRecents() throws Exception {
Set<NotifyingApp> expected = new HashSet<>();
@@ -2964,6 +3003,20 @@
}
@Test
+ public void testVisualDifference_summaryNewNotification() {
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setGroup("bananas")
+ .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+ .setContentText("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(null, r2));
+ }
+
+ @Test
public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
// post 2 notification from this package
final NotificationRecord notif1 = generateNotificationRecord(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 942a07a..96ac935 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -121,7 +121,7 @@
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 1;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
@@ -149,7 +149,7 @@
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 22;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
@@ -250,7 +250,7 @@
calAlarm.add(Calendar.DATE, 1); // add a day
// ScheduleInfo: day 1, day 2: 9pm-7am
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 21;
mScheduleInfo.endHour = 7;
mScheduleInfo.startMinute = 0;
@@ -418,7 +418,7 @@
now.set(Calendar.MILLISECOND, 0);
now.add(Calendar.DATE, 1); // add a day
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 22;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endHour = 3;
@@ -446,7 +446,7 @@
now.set(Calendar.MILLISECOND, 0);
now.add(Calendar.DATE, 1); // add a day
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 22;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endHour = 3;
@@ -464,4 +464,10 @@
private int getTodayDay() {
return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
}
+
+ private int getTodayDay(int offset) {
+ Calendar cal = new GregorianCalendar();
+ cal.add(Calendar.DATE, offset);
+ return cal.get(Calendar.DAY_OF_WEEK);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 43a4e27..1db8967 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -59,7 +60,7 @@
public class SliceManagerServiceTest extends UiServiceTestCase {
private static final String AUTH = "com.android.services.uitests";
- private static final Uri TEST_URI = maybeAddUserId(Uri.parse("content://" + AUTH + "/path"), 0);
+ private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path");
private static final SliceSpec[] EMPTY_SPECS = new SliceSpec[]{
};
@@ -93,7 +94,7 @@
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
- verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), anyString());
+ verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
}
@Test
@@ -126,4 +127,19 @@
verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
}
+ @Test(expected = IllegalStateException.class)
+ public void testNoPinThrow() throws Exception {
+ mService.getPinnedSpecs(TEST_URI, "pkg");
+ }
+
+ @Test
+ public void testGetPinnedSpecs() throws Exception {
+ SliceSpec[] specs = new SliceSpec[] {
+ new SliceSpec("Something", 1) };
+ mService.pinSlice("pkg", TEST_URI, specs, mToken);
+
+ when(mCreatedSliceState.getSpecs()).thenReturn(specs);
+ assertEquals(specs, mService.getPinnedSpecs(TEST_URI, "pkg"));
+ }
+
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 1d5eece..b8dfd38 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -866,7 +866,7 @@
final long token = Binder.clearCallingIdentity();
try {
return UsageStatsService.this.queryEventsForPackage(userId, beginTime,
- endTime, callingPackage);
+ endTime, pkg);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index aa832ad..6a1e97a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -68,6 +68,7 @@
private static final String SHORTCUT_ID_ATTR = "shortcutId";
private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount";
+ private static final String NOTIFICATION_CHANNEL_ATTR = "notificationChannel";
// Time attributes stored as an offset of the beginTime.
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
@@ -189,6 +190,11 @@
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ final String channelId =
+ XmlUtils.readStringAttribute(parser, NOTIFICATION_CHANNEL_ATTR);
+ event.mNotificationChannelId = (channelId != null) ? channelId.intern() : null;
+ break;
}
if (statsOut.events == null) {
@@ -307,6 +313,13 @@
if (event.mBucketAndReason != 0) {
XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
}
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId != null) {
+ XmlUtils.writeStringAttribute(
+ xml, NOTIFICATION_CHANNEL_ATTR, event.mNotificationChannelId);
+ }
+ break;
}
xml.endTag(null, EVENT_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9cb98f3..4efe0b5 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -604,6 +604,9 @@
pw.printPair("standbyBucket", event.getStandbyBucket());
pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
}
+ if (event.mNotificationChannelId != null) {
+ pw.printPair("channelId", event.mNotificationChannelId);
+ }
pw.printHexPair("flags", event.mFlags);
pw.println();
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9f8042c..63945a9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -20,7 +20,8 @@
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
+
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -98,7 +99,7 @@
/**
* UsbDeviceManager manages USB state in device mode.
*/
-public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver {
+public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObserver {
private static final String TAG = UsbDeviceManager.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -386,7 +387,7 @@
public void systemReady() {
if (DEBUG) Slog.d(TAG, "systemReady");
- LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
+ LocalServices.getService(ActivityTaskManagerInternal.class).registerScreenObserver(this);
mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 44f5551..c5d6dc7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -88,6 +89,7 @@
final ContentResolver mResolver;
final DatabaseHelper mDbHelper;
final ActivityManagerInternal mAmInternal;
+ final ActivityTaskManagerInternal mAtmInternal;
final UserManager mUserManager;
final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>();
ShortcutServiceInternal mShortcutServiceInternal;
@@ -104,6 +106,8 @@
mServiceStub = new VoiceInteractionManagerServiceStub();
mAmInternal = Preconditions.checkNotNull(
LocalServices.getService(ActivityManagerInternal.class));
+ mAtmInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ActivityTaskManagerInternal.class));
mUserManager = Preconditions.checkNotNull(
context.getSystemService(UserManager.class));
@@ -212,7 +216,7 @@
@Override
public void onShown() {
- mAmInternal.onLocalVoiceInteractionStarted(token,
+ mAtmInternal.onLocalVoiceInteractionStarted(token,
mImpl.mActiveSession.mSession,
mImpl.mActiveSession.mInteractor);
}
@@ -429,11 +433,11 @@
if (hasComponent) {
mShortcutServiceInternal.setShortcutHostPackage(TAG,
serviceComponent.getPackageName(), mCurUser);
- mAmInternal.setAllowAppSwitches(TAG,
+ mAtmInternal.setAllowAppSwitches(TAG,
serviceInfo.applicationInfo.uid, mCurUser);
} else {
mShortcutServiceInternal.setShortcutHostPackage(TAG, null, mCurUser);
- mAmInternal.setAllowAppSwitches(TAG, -1, mCurUser);
+ mAtmInternal.setAllowAppSwitches(TAG, -1, mCurUser);
}
}
@@ -1181,7 +1185,7 @@
private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
mImpl = impl;
- mAmInternal.notifyActiveVoiceInteractionServiceChanged(
+ mAtmInternal.notifyActiveVoiceInteractionServiceChanged(
getActiveServiceComponentName());
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ba94afe..92aa152 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -23,9 +23,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -49,7 +49,6 @@
import android.util.Slog;
import android.view.IWindowManager;
-import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.LocalServices;
@@ -171,7 +170,7 @@
activityTokens.add(activityToken);
} else {
// Let's get top activities from all visible stacks
- activityTokens = LocalServices.getService(ActivityManagerInternal.class)
+ activityTokens = LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
}
return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 457bfcd..01f5d9c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -16,9 +16,9 @@
package com.android.server.voiceinteraction;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
@@ -144,7 +144,7 @@
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mAppOps = context.getSystemService(AppOpsManager.class);
- mAssistDataRequester = new AssistDataRequester(mContext, mAm, mIWindowManager,
+ mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
IBinder permOwner = null;
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 2d096c0..8283e97 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3542,6 +3542,12 @@
public static final String ICCID_PREFIX = "iccid_prefix";
/**
+ * Certificate for carrier privilege access rules.
+ * <P>Type: TEXT in hex string </P>
+ */
+ public static final String PRIVILEGE_ACCESS_RULE = "privilege_access_rule";
+
+ /**
* The {@code content://} URI for this table.
*/
public static final Uri CONTENT_URI = Uri.parse("content://carrier_id/all");
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index f7e6840..4354314 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -36,8 +36,8 @@
/**
* Base class of network service. Services that extend NetworkService must register the service in
* their AndroidManifest to be detected by the framework. They must be protected by the permission
- * "android.permission.BIND_NETWORK_SERVICE". The network service definition in the manifest must
- * follow the following format:
+ * "android.permission.BIND_TELEPHONY_NETWORK_SERVICE". The network service definition in the
+ * manifest must follow the following format:
* ...
* <service android:name=".xxxNetworkService"
* android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d7568b4..102d5a0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -335,11 +335,18 @@
* <p>
* The {@link #EXTRA_STATE} extra indicates the new call state.
* If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second
- * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outoing calls
- * as a String. Note: If the receiving app has
+ * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outgoing
+ * calls as a String.
+ * <p>
+ * If the receiving app has
* {@link android.Manifest.permission#READ_CALL_LOG} and
* {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the
- * broadcast twice; one with the phone number and another without it.
+ * broadcast twice; one with the {@link #EXTRA_INCOMING_NUMBER} populated with the phone number,
+ * and another with it blank. Due to the nature of broadcasts, you cannot assume the order
+ * in which these broadcasts will arrive, however you are guaranteed to receive two in this
+ * case. Apps which are interested in the {@link #EXTRA_INCOMING_NUMBER} can ignore the
+ * broadcasts where {@link #EXTRA_INCOMING_NUMBER} is not present in the extras (e.g. where
+ * {@link Intent#hasExtra(String)} returns {@code false}).
* <p class="note">
* This was a {@link android.content.Context#sendStickyBroadcast sticky}
* broadcast in version 1.0, but it is no longer sticky.
@@ -488,10 +495,19 @@
public static final String EXTRA_STATE_OFFHOOK = PhoneConstants.State.OFFHOOK.toString();
/**
- * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
- * for a String containing the incoming phone number.
- * Only valid when the new call state is RINGING.
- *
+ * Extra key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+ * for a String containing the incoming or outgoing phone number.
+ * <p>
+ * This extra is only populated for receivers of the {@link #ACTION_PHONE_STATE_CHANGED}
+ * broadcast which have been granted the {@link android.Manifest.permission#READ_CALL_LOG} and
+ * {@link android.Manifest.permission#READ_PHONE_STATE} permissions.
+ * <p>
+ * For incoming calls, the phone number is only guaranteed to be populated when the
+ * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_RINGING}.
+ * If the incoming caller is from an unknown number, the extra will be populated with an empty
+ * string.
+ * For outgoing calls, the phone number is only guaranteed to be populated when the
+ * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_OFFHOOK}.
* <p class="note">
* Retrieve with
* {@link android.content.Intent#getStringExtra(String)}.
@@ -5432,23 +5448,6 @@
}
/**
- * @return true if the IMS resolver is busy resolving a binding and should not be considered
- * available, false if the IMS resolver is idle.
- * @hide
- */
- public boolean isResolvingImsBinding() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.isResolvingImsBinding();
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
- }
- return false;
- }
-
- /**
* Set IMS registration state
*
* @param Registration state
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 4ca5ce3..1db5850 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -44,11 +44,11 @@
/**
* Base class of data service. Services that extend DataService must register the service in
* their AndroidManifest to be detected by the framework. They must be protected by the permission
- * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
- * the following format:
+ * "android.permission.BIND_TELEPHONY_DATA_SERVICE". The data service definition in the manifest
+ * must follow the following format:
* ...
* <service android:name=".xxxDataService"
- * android:permission="android.permission.BIND_DATA_SERVICE" >
+ * android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE" >
* <intent-filter>
* <action android:name="android.telephony.data.DataService" />
* </intent-filter>
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index d999c13..e546917 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -63,34 +63,25 @@
public static final int EVENT_RADIO_AVAILABLE = BASE + 1;
public static final int EVENT_RECORDS_LOADED = BASE + 2;
public static final int EVENT_TRY_SETUP_DATA = BASE + 3;
- public static final int EVENT_DATA_STATE_CHANGED = BASE + 4;
- public static final int EVENT_POLL_PDP = BASE + 5;
public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
public static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
public static final int EVENT_VOICE_CALL_ENDED = BASE + 8;
public static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9;
- public static final int EVENT_LINK_STATE_CHANGED = BASE + 10;
public static final int EVENT_ROAMING_ON = BASE + 11;
public static final int EVENT_ROAMING_OFF = BASE + 12;
public static final int EVENT_ENABLE_NEW_APN = BASE + 13;
- public static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
public static final int EVENT_DISCONNECT_DONE = BASE + 15;
public static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
public static final int EVENT_DATA_STALL_ALARM = BASE + 17;
public static final int EVENT_DO_RECOVERY = BASE + 18;
public static final int EVENT_APN_CHANGED = BASE + 19;
- public static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
- public static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = BASE + 21;
public static final int EVENT_PS_RESTRICT_ENABLED = BASE + 22;
public static final int EVENT_PS_RESTRICT_DISABLED = BASE + 23;
public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
- public static final int EVENT_CDMA_OTA_PROVISION = BASE + 25;
public static final int EVENT_RESTART_RADIO = BASE + 26;
public static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
- public static final int EVENT_RESET_DONE = BASE + 28;
public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
public static final int CMD_SET_USER_DATA_ENABLE = BASE + 30;
- public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
public static final int EVENT_ICC_CHANGED = BASE + 33;
public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6ea8ccb..84a18b4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -817,12 +817,6 @@
IImsConfig getImsConfig(int slotId, int feature);
/**
- * @return true if the IMS resolver is busy resolving a binding and should not be considered
- * available, false if the IMS resolver is idle.
- */
- boolean isResolvingImsBinding();
-
- /**
* @return true if the ImsService to bind to for the slot id specified was set, false otherwise.
*/
boolean setImsService(int slotId, boolean isCarrierImsService, String packageName);
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 0248e97..9838020 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -314,9 +314,9 @@
// Test multiply.
gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
+ gen.addLoadImmediate(Register.R0, 123456789);
gen.addMul(2);
- gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
assertDrop(gen);
// Test divide.
@@ -379,10 +379,10 @@
// Test multiply.
gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
+ gen.addLoadImmediate(Register.R0, 123456789);
gen.addLoadImmediate(Register.R1, 2);
gen.addMulR1();
- gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
+ gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
assertDrop(gen);
// Test divide.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2208580..dbf81d6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -57,6 +57,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -95,6 +96,7 @@
import android.net.ConnectivityThread;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -125,6 +127,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -132,6 +135,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -145,6 +149,7 @@
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.Vpn;
@@ -161,10 +166,13 @@
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -190,6 +198,7 @@
private static final int TIMEOUT_MS = 500;
private static final int TEST_LINGER_DELAY_MS = 120;
+ private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -950,6 +959,10 @@
return monitor;
}
+ public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
+ return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+ }
+
@Override
public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
Context c, Handler h, Runnable r) {
@@ -4422,4 +4435,97 @@
mMockVpn.disconnect();
}
+
+ /**
+ * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
+ */
+ private InterfaceConfiguration getClatInterfaceConfig(LinkAddress la) {
+ InterfaceConfiguration cfg = new InterfaceConfiguration();
+ cfg.setHardwareAddress("11:22:33:44:55:66");
+ cfg.setLinkAddress(la);
+ return cfg;
+ }
+
+ /**
+ * Make expected stack link properties, copied from Nat464Xlat.
+ */
+ private LinkProperties makeClatLinkProperties(LinkAddress la) {
+ LinkAddress clatAddress = la;
+ LinkProperties stacked = new LinkProperties();
+ stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME);
+ RouteInfo ipv4Default = new RouteInfo(
+ new LinkAddress(Inet4Address.ANY, 0),
+ clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME);
+ stacked.addRoute(ipv4Default);
+ stacked.addLinkAddress(clatAddress);
+ return stacked;
+ }
+
+ @Test
+ public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
+ final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
+ final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ // Prepare ipv6 only link properties and connect.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ cellLp.addLinkAddress(myIpv6);
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
+ cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+ reset(mNetworkManagementService);
+ when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfig(myIpv4));
+
+ // Connect with ipv6 link properties, then expect clat setup ipv4 and update link
+ // properties properly.
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ mCellNetworkAgent.connect(true);
+ networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME);
+ Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+
+ // Clat iface up, expect stack link updated.
+ clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ waitForIdle();
+ List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
+ .getStackedLinks();
+ assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+
+ // Change trivial linkproperties and see if stacked link is preserved.
+ cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+
+ List<LinkProperties> stackedLpsAfterChange =
+ mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
+ assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
+ assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
+
+ // Add ipv4 address, expect stacked linkproperties be cleaned up
+ cellLp.addLinkAddress(myIpv4);
+ cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME);
+
+ // Clat iface removed, expect linkproperties revert to original one
+ clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+ waitForIdle();
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
+ assertEquals(cellLp, actualLpAfterIpv4);
+
+ // Clean up
+ mCellNetworkAgent.disconnect();
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 34ecde8..0d3b8e4 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -71,6 +71,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
@@ -128,6 +129,10 @@
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ // Actual contents of the request don't matter for this test. The lack of
+ // any specific TRANSPORT_* is sufficient to identify this request.
+ private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
@@ -238,6 +243,11 @@
isTetheringSupportedCalls++;
return true;
}
+
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ return mDefaultRequest;
+ }
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
@@ -305,6 +315,8 @@
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
+ when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
+ .thenReturn(false);
when(mNMService.listInterfaces())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
@@ -458,6 +470,7 @@
}
private void prepareUsbTethering(NetworkState upstreamState) {
+ when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
@@ -519,7 +532,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
- verify(mUpstreamNetworkMonitor, times(1)).start();
+ verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
@@ -656,6 +669,24 @@
}
@Test
+ public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
+ when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
+ .thenReturn(true);
+ sendConfigurationChanged();
+
+ // Setup IPv6
+ final NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ runUsbTethering(upstreamState);
+
+ // UpstreamNetworkMonitor should choose upstream automatically
+ // (in this specific case: choose the default network).
+ verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
+ verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
+
+ verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
+ }
+
+ @Test
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
workingLocalOnlyHotspotEnrichedApBroadcast(true);
}
@@ -718,7 +749,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
- verify(mUpstreamNetworkMonitor, times(1)).start();
+ verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 9661dc2..3e21a2c 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -73,6 +74,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
@@ -84,6 +86,10 @@
private static final boolean INCLUDES = true;
private static final boolean EXCLUDES = false;
+ // Actual contents of the request don't matter for this test. The lack of
+ // any specific TRANSPORT_* is sufficient to identify this request.
+ private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+
@Mock private Context mContext;
@Mock private IConnectivityManager mCS;
@Mock private SharedLog mLog;
@@ -113,6 +119,13 @@
}
@Test
+ public void testStopWithoutStartIsNonFatal() {
+ mUNM.stop();
+ mUNM.stop();
+ mUNM.stop();
+ }
+
+ @Test
public void testDoesNothingBeforeStarted() {
assertTrue(mCM.hasNoCallbacks());
assertFalse(mUNM.mobileNetworkRequested());
@@ -127,7 +140,7 @@
public void testDefaultNetworkIsTracked() throws Exception {
assertEquals(0, mCM.trackingDefault.size());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertEquals(1, mCM.trackingDefault.size());
mUNM.stop();
@@ -138,7 +151,7 @@
public void testListensForAllNetworks() throws Exception {
assertTrue(mCM.listening.isEmpty());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertFalse(mCM.listening.isEmpty());
assertTrue(mCM.isListeningForAll());
@@ -148,9 +161,11 @@
@Test
public void testCallbacksRegistered() {
- mUNM.start();
- verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
- verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+ mUNM.start(mDefaultRequest);
+ verify(mCM, times(1)).registerNetworkCallback(
+ any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+ verify(mCM, times(1)).requestNetwork(
+ eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
mUNM.stop();
verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
@@ -161,7 +176,7 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -184,17 +199,17 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start();
- verify(mCM, Mockito.times(1)).registerNetworkCallback(
+ mUNM.start(mDefaultRequest);
+ verify(mCM, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
- any(NetworkCallback.class), any(Handler.class));
+ verify(mCM, times(1)).requestNetwork(
+ eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.updateMobileRequiresDun(true);
mUNM.registerMobileNetworkRequest();
- verify(mCM, Mockito.times(1)).requestNetwork(
+ verify(mCM, times(1)).requestNetwork(
any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
any(Handler.class));
@@ -222,7 +237,7 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -242,7 +257,7 @@
@Test
public void testUpdateMobileRequiresDun() throws Exception {
- mUNM.start();
+ mUNM.start(mDefaultRequest);
// Test going from no-DUN to DUN correctly re-registers callbacks.
mUNM.updateMobileRequiresDun(false);
@@ -270,7 +285,7 @@
final Collection<Integer> preferredTypes = new ArrayList<>();
preferredTypes.add(TYPE_WIFI);
- mUNM.start();
+ mUNM.start(mDefaultRequest);
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -334,8 +349,47 @@
}
@Test
+ public void testGetCurrentPreferredUpstream() throws Exception {
+ mUNM.start(mDefaultRequest);
+ mUNM.updateMobileRequiresDun(false);
+
+ // [0] Mobile connects, DUN not required -> mobile selected.
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ cellAgent.fakeConnect();
+ mCM.makeDefaultNetwork(cellAgent);
+ assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [1] WiFi connects but not validated/promoted to default -> mobile selected.
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ wifiAgent.fakeConnect();
+ assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [2] WiFi validates and is promoted to the default network -> WiFi selected.
+ mCM.makeDefaultNetwork(wifiAgent);
+ assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [3] DUN required, no other changes -> WiFi still selected
+ mUNM.updateMobileRequiresDun(true);
+ assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [4] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
+ mCM.makeDefaultNetwork(cellAgent);
+ assertEquals(null, mUNM.getCurrentPreferredUpstream());
+ // TODO: make sure that a DUN request has been filed. This is currently
+ // triggered by code over in Tethering, but once that has been moved
+ // into UNM we should test for this here.
+
+ // [5] DUN network arrives -> DUN selected
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
+ dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+ dunAgent.fakeConnect();
+ assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+ }
+
+ @Test
public void testLocalPrefixes() throws Exception {
- mUNM.start();
+ mUNM.start(mDefaultRequest);
// [0] Test minimum set of local prefixes.
Set<IpPrefix> local = mUNM.getLocalPrefixes();
@@ -345,7 +399,7 @@
// [1] Pretend Wi-Fi connects.
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
- final LinkProperties wifiLp = new LinkProperties();
+ final LinkProperties wifiLp = wifiAgent.linkProperties;
wifiLp.setInterfaceName("wlan0");
final String[] WIFI_ADDRS = {
"fe80::827a:bfff:fe6f:374d", "100.112.103.18",
@@ -358,7 +412,7 @@
wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
wifiAgent.fakeConnect();
- wifiAgent.sendLinkProperties(wifiLp);
+ wifiAgent.sendLinkProperties();
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -372,7 +426,7 @@
// [2] Pretend mobile connects.
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- final LinkProperties cellLp = new LinkProperties();
+ final LinkProperties cellLp = cellAgent.linkProperties;
cellLp.setInterfaceName("rmnet_data0");
final String[] CELL_ADDRS = {
"10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
@@ -382,7 +436,7 @@
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
cellAgent.fakeConnect();
- cellAgent.sendLinkProperties(cellLp);
+ cellAgent.sendLinkProperties();
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -394,17 +448,18 @@
// [3] Pretend DUN connects.
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
- final LinkProperties dunLp = new LinkProperties();
+ dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+ final LinkProperties dunLp = dunAgent.linkProperties;
dunLp.setInterfaceName("rmnet_data1");
final String[] DUN_ADDRS = {
"192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
};
for (String addrStr : DUN_ADDRS) {
final String cidr = addrStr.contains(":") ? "/64" : "/27";
- cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
+ dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
dunAgent.fakeConnect();
- dunAgent.sendLinkProperties(dunLp);
+ dunAgent.sendLinkProperties();
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -442,6 +497,7 @@
public static class TestConnectivityManager extends ConnectivityManager {
public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
public Set<NetworkCallback> trackingDefault = new HashSet<>();
+ public TestNetworkAgent defaultNetwork = null;
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
@@ -483,12 +539,34 @@
int getNetworkId() { return ++mNetworkId; }
+ void makeDefaultNetwork(TestNetworkAgent agent) {
+ if (Objects.equals(defaultNetwork, agent)) return;
+
+ final TestNetworkAgent formerDefault = defaultNetwork;
+ defaultNetwork = agent;
+
+ for (NetworkCallback cb : trackingDefault) {
+ if (defaultNetwork != null) {
+ cb.onAvailable(defaultNetwork.networkId);
+ cb.onCapabilitiesChanged(
+ defaultNetwork.networkId, defaultNetwork.networkCapabilities);
+ cb.onLinkPropertiesChanged(
+ defaultNetwork.networkId, defaultNetwork.linkProperties);
+ }
+ }
+ }
+
@Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
assertFalse(allCallbacks.containsKey(cb));
allCallbacks.put(cb, h);
- assertFalse(requested.containsKey(cb));
- requested.put(cb, req);
+ if (mDefaultRequest.equals(req)) {
+ assertFalse(trackingDefault.contains(cb));
+ trackingDefault.add(cb);
+ } else {
+ assertFalse(requested.containsKey(cb));
+ requested.put(cb, req);
+ }
}
@Override
@@ -524,10 +602,7 @@
@Override
public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
- assertFalse(allCallbacks.containsKey(cb));
- allCallbacks.put(cb, h);
- assertFalse(trackingDefault.contains(cb));
- trackingDefault.add(cb);
+ fail("Should never be called.");
}
@Override
@@ -561,6 +636,7 @@
public final Network networkId;
public final int transportType;
public final NetworkCapabilities networkCapabilities;
+ public final LinkProperties linkProperties;
public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
this.cm = cm;
@@ -569,12 +645,14 @@
networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(transportType);
networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ linkProperties = new LinkProperties();
}
public void fakeConnect() {
for (NetworkCallback cb : cm.listening.keySet()) {
cb.onAvailable(networkId);
cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
+ cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
}
}
@@ -584,11 +662,16 @@
}
}
- public void sendLinkProperties(LinkProperties lp) {
+ public void sendLinkProperties() {
for (NetworkCallback cb : cm.listening.keySet()) {
- cb.onLinkPropertiesChanged(networkId, lp);
+ cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
}
}
+
+ @Override
+ public String toString() {
+ return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
+ }
}
public static class TestStateMachine extends StateMachine {
@@ -618,6 +701,10 @@
return new NetworkCapabilities(nc);
}
+ static LinkProperties copy(LinkProperties lp) {
+ return new LinkProperties(lp);
+ }
+
static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
final Set<String> expectedSet = new HashSet<>();
Collections.addAll(expectedSet, expected);
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index ef5912b..8719a23 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -208,6 +208,15 @@
}
};
+// A chunk of text in the XML string within a CDATA tags.
+class CdataSegmentNode : public SegmentNode {
+ public:
+
+ void Build(StringBuilder* builder) const override {
+ builder->AppendText(data, /* preserve_spaces */ true);
+ }
+};
+
// A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
class SpanNode : public Node {
public:
@@ -244,6 +253,7 @@
std::vector<Node*> node_stack;
node_stack.push_back(&root);
+ bool cdata_block = false;
bool saw_span_node = false;
SegmentNode* first_segment = nullptr;
SegmentNode* last_segment = nullptr;
@@ -253,11 +263,15 @@
const xml::XmlPullParser::Event event = parser->event();
// First take care of any SegmentNodes that should be created.
- if (event == xml::XmlPullParser::Event::kStartElement ||
- event == xml::XmlPullParser::Event::kEndElement) {
+ if (event == xml::XmlPullParser::Event::kStartElement
+ || event == xml::XmlPullParser::Event::kEndElement
+ || event == xml::XmlPullParser::Event::kCdataStart
+ || event == xml::XmlPullParser::Event::kCdataEnd) {
if (!current_text.empty()) {
- std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
+ std::unique_ptr<SegmentNode> segment_node = (cdata_block)
+ ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>();
segment_node->data = std::move(current_text);
+
last_segment = node_stack.back()->AddChild(std::move(segment_node));
if (first_segment == nullptr) {
first_segment = last_segment;
@@ -333,6 +347,16 @@
}
} break;
+ case xml::XmlPullParser::Event::kCdataStart: {
+ cdata_block = true;
+ break;
+ }
+
+ case xml::XmlPullParser::Event::kCdataEnd: {
+ cdata_block = false;
+ break;
+ }
+
default:
// ignore.
break;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 5711dc3..ee496d5 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -989,4 +989,40 @@
ASSERT_FALSE(TestParse(input));
}
+TEST_F(ResourceParserTest, ParseCData) {
+ std::string input = R"(
+ <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)";
+
+ ASSERT_TRUE(TestParse(input));
+ String* output = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe"));
+
+ // Double quotes should not change the state of whitespace processing
+ input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]> World</string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo2");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>' World").data()));
+
+ // Cdata blocks should not have their whitespace trimmed
+ input = R"(<string name="foo3"> <![CDATA[ text ]]> </string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo3");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string(" text ").data()));
+
+ input = R"(<string name="foo4"> <![CDATA[]]> </string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo4");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string("").data()));
+
+ input = R"(<string name="foo5"> <![CDATA[ ]]> </string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo5");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string(" ").data()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 560077c..c48765b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -797,16 +797,20 @@
: preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
}
-StringBuilder& StringBuilder::AppendText(const std::string& text) {
+StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) {
if (!error_.empty()) {
return *this;
}
+ // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed
+ // to preserve spaces
+ preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_;
+
const size_t previous_len = xml_string_.text.size();
Utf8Iterator iter(text);
while (iter.HasNext()) {
char32_t codepoint = iter.Next();
- if (!quote_ && iswspace(codepoint)) {
+ if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
if (!last_codepoint_was_space_) {
// Emit a space if it's the first.
xml_string_.text += ' ';
@@ -827,7 +831,6 @@
case U't':
xml_string_.text += '\t';
break;
-
case U'n':
xml_string_.text += '\n';
break;
@@ -855,12 +858,12 @@
break;
}
}
- } else if (!preserve_spaces_ && codepoint == U'"') {
+ } else if (!preserve_spaces && codepoint == U'"') {
// Only toggle the quote state when we are not preserving spaces.
quote_ = !quote_;
- } else if (!quote_ && codepoint == U'\'') {
- // This should be escaped.
+ } else if (!preserve_spaces && !quote_ && codepoint == U'\'') {
+ // This should be escaped when we are not preserving spaces
error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
return *this;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 7af2fe0..410ef28 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -267,8 +267,10 @@
// single quotations can be used without escaping them.
explicit StringBuilder(bool preserve_spaces = false);
- // Appends a chunk of text.
- StringBuilder& AppendText(const std::string& text);
+ // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and
+ // single quotations can be used without escaping them for this append. Otherwise, the
+ // StringBuilder will behave as it was constructed.
+ StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false);
// Starts a Span (tag) with the given name. The name is expected to be of the form:
// "tag_name;attr1=value;attr2=value;"
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 11f3fa3..5ce4640 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -254,6 +254,29 @@
TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
Eq("\""));
+
+ // Single quotes should be able to be used without escaping them when preserving spaces and the
+ // spaces should not be trimmed
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys ")
+ .AppendText(" 'this is so cool' ", /* preserve_spaces */ true)
+ .AppendText(" wow ")
+ .to_string(),
+ Eq(" hey guys 'this is so cool' wow "));
+
+ // Reading a double quote while preserving spaces should not change the quote state
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys ")
+ .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+ .AppendText(" wow ")
+ .to_string(),
+ Eq(" hey guys \"this is so cool' wow "));
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys\" ")
+ .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+ .AppendText(" wow \" ")
+ .to_string(),
+ Eq(" hey guys \"this is so cool' wow "));
}
} // namespace aapt
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
index 021185f..c723d90 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
@@ -23,6 +23,7 @@
LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
# We need this to retain the R.java generated for this library.
LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
index 39bd481..90a7f62 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
@@ -24,6 +24,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
# We need this to retain the R.java generated for this library.
LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 402e5a4..a023494 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -38,6 +38,7 @@
EndNamespaceHandler);
XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
XML_SetCommentHandler(parser_, CommentDataHandler);
+ XML_SetCdataSectionHandler(parser_, StartCdataSectionHandler, EndCdataSectionHandler);
event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
}
@@ -287,6 +288,22 @@
parser->depth_, comment});
}
+void XMLCALL XmlPullParser::StartCdataSectionHandler(void* user_data) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(EventData{Event::kCdataStart,
+ XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_ });
+}
+
+void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(EventData{Event::kCdataEnd,
+ XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_ });
+}
+
Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 63db66f..6ebaa28 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -52,6 +52,8 @@
kEndElement,
kText,
kComment,
+ kCdataStart,
+ kCdataEnd,
};
/**
@@ -159,6 +161,8 @@
static void XMLCALL EndElementHandler(void* user_data, const char* name);
static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
+ static void XMLCALL StartCdataSectionHandler(void* user_data);
+ static void XMLCALL EndCdataSectionHandler(void* user_data);
struct EventData {
Event event;
@@ -223,6 +227,10 @@
return out << "Text";
case XmlPullParser::Event::kComment:
return out << "Comment";
+ case XmlPullParser::Event::kCdataStart:
+ return out << "CdataStart";
+ case XmlPullParser::Event::kCdataEnd:
+ return out << "CdataEnd";
}
return out;
}
@@ -240,6 +248,8 @@
case Event::kText:
case Event::kComment:
case Event::kStartElement:
+ case Event::kCdataStart:
+ case Event::kCdataEnd:
return true;
default:
break;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 66ccc6c..af44b7e 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -147,6 +147,8 @@
boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
+ void notifyUserOfApBandConversion(String packageName);
+
Messenger getWifiServiceMessenger(String packageName);
void enableTdls(String remoteIPAddress, boolean enable);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 25f35d0..6963ed0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2170,6 +2170,21 @@
}
/**
+ * Method that triggers a notification to the user about a conversion to their saved AP config.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void notifyUserOfApBandConversion() {
+ Log.d(TAG, "apBand was converted, notify the user");
+ try {
+ mService.notifyUserOfApBandConversion(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Enable/Disable TDLS on a specific local route.
*
* <p>