Merge "Remove useless open modes"
diff --git a/api/current.txt b/api/current.txt
index 97facec..80c3ac2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10887,6 +10887,7 @@
field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
+ field public static final int PROTECTION_FLAG_INSTANT = 4096; // 0x1000
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
diff --git a/api/system-current.txt b/api/system-current.txt
index 2212fd0..0398a02 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10903,7 +10903,6 @@
method public int getVersionCode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR;
- field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
}
public static final class InstantAppResolveInfo.InstantAppDigest implements android.os.Parcelable {
@@ -11604,8 +11603,8 @@
field public static final int PROTECTION_DANGEROUS = 1; // 0x1
field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
- field public static final int PROTECTION_FLAG_EPHEMERAL = 4096; // 0x1000
field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
+ field public static final int PROTECTION_FLAG_INSTANT = 4096; // 0x1000
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
diff --git a/api/test-current.txt b/api/test-current.txt
index 9ee2527..c5b6cb2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10928,8 +10928,8 @@
field public static final int PROTECTION_DANGEROUS = 1; // 0x1
field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
- field public static final int PROTECTION_FLAG_EPHEMERAL = 4096; // 0x1000
field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
+ field public static final int PROTECTION_FLAG_INSTANT = 4096; // 0x1000
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
index 664c48f..fba5e66 100644
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ b/cmds/incident_helper/IncidentHelper.cpp
@@ -29,8 +29,37 @@
using namespace android::base;
using namespace android::os;
+using namespace google::protobuf;
using namespace std;
+static bool
+SetTableField(::google::protobuf::Message* message, string field_name, string field_value) {
+ const Descriptor* descriptor = message->GetDescriptor();
+ const Reflection* reflection = message->GetReflection();
+
+ const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
+ switch (field->type()) {
+ case FieldDescriptor::TYPE_STRING:
+ reflection->SetString(message, field, field_value);
+ return true;
+ case FieldDescriptor::TYPE_INT64:
+ reflection->SetInt64(message, field, atol(field_value.c_str()));
+ return true;
+ case FieldDescriptor::TYPE_UINT64:
+ reflection->SetUInt64(message, field, atol(field_value.c_str()));
+ return true;
+ case FieldDescriptor::TYPE_INT32:
+ reflection->SetInt32(message, field, atoi(field_value.c_str()));
+ return true;
+ case FieldDescriptor::TYPE_UINT32:
+ reflection->SetUInt32(message, field, atoi(field_value.c_str()));
+ return true;
+ default:
+ // Add new scalar types
+ return false;
+ }
+}
+
// ================================================================================
status_t ReverseParser::Parse(const int in, const int out) const
{
@@ -51,31 +80,6 @@
// ================================================================================
static const string KERNEL_WAKEUP_LINE_DELIMITER = "\t";
-static void SetWakeupSourceField(WakeupSourceProto* source, string name, string value) {
- if (name == "name") {
- source->set_name(value.c_str());
- } else if (name == "active_count") {
- source->set_active_count(atoi(value.c_str()));
- } else if (name == "event_count") {
- source->set_event_count(atoi(value.c_str()));
- } else if (name == "wakeup_count") {
- source->set_wakeup_count(atoi(value.c_str()));
- } else if (name == "expire_count") {
- source->set_expire_count(atoi(value.c_str()));
- } else if (name == "active_count") {
- source->set_active_since(atol(value.c_str()));
- } else if (name == "total_time") {
- source->set_total_time(atol(value.c_str()));
- } else if (name == "max_time") {
- source->set_max_time(atol(value.c_str()));
- } else if (name == "last_change") {
- source->set_last_change(atol(value.c_str()));
- } else if (name == "prevent_suspend_time") {
- source->set_prevent_suspend_time(atol(value.c_str()));
- }
- // add new fields
-}
-
status_t KernelWakesParser::Parse(const int in, const int out) const {
Reader reader(in);
string line;
@@ -90,12 +94,12 @@
if (line.empty()) continue;
// parse head line
if (nline++ == 0) {
- split(line, header, KERNEL_WAKEUP_LINE_DELIMITER);
+ header = parseHeader(line, KERNEL_WAKEUP_LINE_DELIMITER);
continue;
}
// parse for each record, the line delimiter is \t only!
- split(line, record, KERNEL_WAKEUP_LINE_DELIMITER);
+ record = parseRecord(line, KERNEL_WAKEUP_LINE_DELIMITER);
if (record.size() != header.size()) {
// TODO: log this to incident report!
@@ -105,7 +109,10 @@
WakeupSourceProto* source = proto.add_wakeup_sources();
for (int i=0; i<(int)record.size(); i++) {
- SetWakeupSourceField(source, header[i], record[i]);
+ if (!SetTableField(source, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
}
}
@@ -123,32 +130,6 @@
}
// ================================================================================
-// Remove K for numeric fields
-static void SetProcessField(ProcessProto* process, string name, string value) {
- ssize_t len = value.size();
- if (name == "PID") {
- process->set_pid(atoi(value.c_str()));
- } else if (name == "Vss") {
- process->set_vss(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "Rss") {
- process->set_rss(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "Pss") {
- process->set_pss(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "Uss") {
- process->set_uss(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "Swap") {
- process->set_swap(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "PSwap") {
- process->set_pswap(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "USwap") {
- process->set_uswap(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "ZSwap") {
- process->set_zswap(atol(value.substr(0, len - 1).c_str()));
- } else if (name == "cmdline") {
- process->set_cmdline(value);
- }
-}
-
status_t ProcrankParser::Parse(const int in, const int out) const {
Reader reader(in);
string line, content;
@@ -164,22 +145,22 @@
// parse head line
if (nline++ == 0) {
- split(line, header);
+ header = parseHeader(line);
continue;
}
- split(line, record);
+ record = parseRecord(line);
if (record.size() != header.size()) {
if (record[record.size() - 1] == "TOTAL") { // TOTAL record
ProcessProto* total = proto.mutable_summary()->mutable_total();
for (int i=1; i<=(int)record.size(); i++) {
- SetProcessField(total, header[header.size() - i], record[record.size() - i]);
+ SetTableField(total, header[header.size() - i], record[record.size() - i]);
}
} else if (record[0] == "ZRAM:") {
- split(line, record, ":");
+ record = parseRecord(line, ":");
proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]);
} else if (record[0] == "RAM:") {
- split(line, record, ":");
+ record = parseRecord(line, ":");
proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]);
} else {
fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
@@ -190,7 +171,10 @@
ProcessProto* process = proto.add_processes();
for (int i=0; i<(int)record.size(); i++) {
- SetProcessField(process, header[i], record[i]);
+ if (!SetTableField(process, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
}
}
diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/ih_util.cpp
index bbb625f..b2fda23 100644
--- a/cmds/incident_helper/ih_util.cpp
+++ b/cmds/incident_helper/ih_util.cpp
@@ -23,16 +23,24 @@
const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
-std::string trim(const std::string& s, const std::string& whitespace) {
- const auto head = s.find_first_not_of(whitespace);
+
+static std::string trim(const std::string& s) {
+ const auto head = s.find_first_not_of(DEFAULT_WHITESPACE);
if (head == std::string::npos) return "";
- const auto tail = s.find_last_not_of(whitespace);
+ const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
return s.substr(head, tail - head + 1);
}
+static std::string trimHeader(const std::string& s) {
+ std::string res = trim(s);
+ std::transform(res.begin(), res.end(), res.begin(), ::tolower);
+ return res;
+}
+
// This is similiar to Split in android-base/file.h, but it won't add empty string
-void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters) {
+static void split(const std::string& line, std::vector<std::string>& words,
+ const trans_func& func, const std::string& delimiters) {
words.clear(); // clear the buffer before split
size_t base = 0;
@@ -40,7 +48,7 @@
while (true) {
found = line.find_first_of(delimiters, base);
if (found != base) {
- std::string word = trim(line.substr(base, found - base));
+ std::string word = (*func) (line.substr(base, found - base));
if (!word.empty()) {
words.push_back(word);
}
@@ -50,13 +58,18 @@
}
}
-bool assertHeaders(const char* expected[], const std::vector<std::string>& actual) {
- for (size_t i = 0; i < actual.size(); i++) {
- if (expected[i] == NULL || std::string(expected[i]) != actual[i]) {
- return false;
- }
- }
- return true;
+header_t parseHeader(const std::string& line, const std::string& delimiters) {
+ header_t header;
+ trans_func f = &trimHeader;
+ split(line, header, f, delimiters);
+ return header;
+}
+
+record_t parseRecord(const std::string& line, const std::string& delimiters) {
+ record_t record;
+ trans_func f = &trim;
+ split(line, record, f, delimiters);
+ return record;
}
Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/ih_util.h
index 9e0c18e..5598eed 100644
--- a/cmds/incident_helper/ih_util.h
+++ b/cmds/incident_helper/ih_util.h
@@ -23,16 +23,13 @@
typedef std::vector<std::string> header_t;
typedef std::vector<std::string> record_t;
+typedef std::string (*trans_func) (const std::string&);
const char DEFAULT_NEWLINE = '\n';
const std::string DEFAULT_WHITESPACE = " \t";
-std::string trim(const std::string& s, const std::string& whitespace = DEFAULT_WHITESPACE);
-
-void split(const std::string& line, std::vector<std::string>& words,
- const std::string& delimiters = DEFAULT_WHITESPACE);
-
-bool assertHeaders(const char* expected[], const std::vector<std::string>& actual);
+header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
+record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
/**
* Reader class reads data from given fd in streaming fashion.
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index 5158e0a..3b9ed40 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -26,25 +26,38 @@
using namespace std;
using ::testing::StrEq;
-TEST(IhUtilTest, Trim) {
- EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw "), StrEq("100 00\toooh \t wqrw"));
- EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw ", " "), StrEq("\t 100 00\toooh \t wqrw"));
-}
-
-TEST(IhUtilTest, Split) {
- vector<string> result, expected;
- split(" \t \t\t ", result);
+TEST(IhUtilTest, ParseHeader) {
+ header_t result, expected;
+ result = parseHeader(" \t \t\t ");
EXPECT_EQ(expected, result);
- split(" \t 100 00\toooh \t wqrw", result);
+ result = parseHeader(" \t 100 00\tOpQ \t wqrw");
+ expected = { "100", "00", "opq", "wqrw" };
+ EXPECT_EQ(expected, result);
+
+ result = parseHeader(" \t 100 00\toooh \t wTF", "\t");
+ expected = { "100 00", "oooh", "wtf" };
+ EXPECT_EQ(expected, result);
+
+ result = parseHeader("123,456,78_9", ",");
+ expected = { "123", "456", "78_9" };
+ EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, ParseRecord) {
+ record_t result, expected;
+ result = parseRecord(" \t \t\t ");
+ EXPECT_EQ(expected, result);
+
+ result = parseRecord(" \t 100 00\toooh \t wqrw");
expected = { "100", "00", "oooh", "wqrw" };
EXPECT_EQ(expected, result);
- split(" \t 100 00\toooh \t wqrw", result, "\t");
+ result = parseRecord(" \t 100 00\toooh \t wqrw", "\t");
expected = { "100 00", "oooh", "wqrw" };
EXPECT_EQ(expected, result);
- split("123,456,78_9", result, ",");
+ result = parseRecord("123,456,78_9", ",");
expected = { "123", "456", "78_9" };
EXPECT_EQ(expected, result);
}
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 9cc47ed..0f6f38e 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -330,11 +330,13 @@
void CommandSection::init(const char* command, va_list args)
{
va_list copied_args;
- va_copy(copied_args, args);
int numOfArgs = 0;
- while(va_arg(args, const char*) != NULL) {
+
+ va_copy(copied_args, args);
+ while(va_arg(copied_args, const char*) != NULL) {
numOfArgs++;
}
+ va_end(copied_args);
// allocate extra 1 for command and 1 for NULL terminator
mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2));
@@ -342,13 +344,12 @@
mCommand[0] = command;
name = command;
for (int i=0; i<numOfArgs; i++) {
- const char* arg = va_arg(copied_args, const char*);
+ const char* arg = va_arg(args, const char*);
mCommand[i+1] = arg;
name += " ";
name += arg;
}
mCommand[numOfArgs+1] = NULL;
- va_end(copied_args);
}
CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index adb3152..1c8a376 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1006,6 +1006,11 @@
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
+ } else {
+ AutofillManager afm = getAutofillManager();
+ if (afm != null) {
+ afm.dismissUi();
+ }
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index e5fe240..68fb7bc 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,8 +30,6 @@
import com.android.internal.app.IVoiceInteractor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -90,11 +88,12 @@
String processName, String abiOverride, int uid, Runnable crashHandler);
/**
- * Acquires a sleep token with the specified tag.
+ * 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);
+ public abstract SleepToken acquireSleepToken(@NonNull String tag, int displayId);
/**
* Sleep tokens cause the activity manager to put the top activity to sleep.
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index dcaf66e..22e994f 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -37,7 +37,7 @@
@SystemApi
public final class InstantAppResolveInfo implements Parcelable {
/** Algorithm that will be used to generate the domain digest */
- public static final String SHA_ALGORITHM = "SHA-256";
+ private static final String SHA_ALGORITHM = "SHA-256";
private final InstantAppDigest mDigest;
private final String mPackageName;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 07ce4e9..998e403 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3240,11 +3240,11 @@
perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
- if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0
+ if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
- outError[0] = "<permission> protectionLevel specifies a non-ephemeral flag but is "
+ outError[0] = "<permission> protectionLevel specifies a non-instnat flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 71a6321..b84c1b9 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -122,13 +122,10 @@
/**
* Additional flag for {@link #protectionLevel}, corresponding
- * to the <code>ephemeral</code> value of
+ * to the <code>instant</code> value of
* {@link android.R.attr#protectionLevel}.
- * @hide
*/
- @SystemApi
- @TestApi
- public static final int PROTECTION_FLAG_EPHEMERAL = 0x1000;
+ public static final int PROTECTION_FLAG_INSTANT = 0x1000;
/**
* Additional flag for {@link #protectionLevel}, corresponding
@@ -263,8 +260,8 @@
if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
protLevel += "|setup";
}
- if ((level&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0) {
- protLevel += "|ephemeral";
+ if ((level&PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
+ protLevel += "|instant";
}
if ((level&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
protLevel += "|runtime";
diff --git a/core/java/android/net/ITetheringStatsProvider.aidl b/core/java/android/net/ITetheringStatsProvider.aidl
index 769086d..1aeabc1 100644
--- a/core/java/android/net/ITetheringStatsProvider.aidl
+++ b/core/java/android/net/ITetheringStatsProvider.aidl
@@ -19,7 +19,7 @@
import android.net.NetworkStats;
/**
- * Interface that allows NetworkManagementService to query for tethering statistics.
+ * Interface for NetworkManagementService to query tethering statistics and set data limits.
*
* TODO: this does not really need to be an interface since Tethering runs in the same process
* as NetworkManagementService. Consider refactoring Tethering to use direct access to
@@ -29,5 +29,14 @@
* @hide
*/
interface ITetheringStatsProvider {
+ // Returns cumulative statistics for all tethering sessions since boot, on all upstreams.
NetworkStats getTetherStats();
+
+ // Sets the interface quota for the specified upstream interface. This is defined as the number
+ // of bytes, starting from zero and counting from now, after which data should stop being
+ // forwarded to/from the specified upstream. A value of QUOTA_UNLIMITED means there is no limit.
+ void setInterfaceQuota(String iface, long quotaBytes);
+
+ // Indicates that no data usage limit is set.
+ const int QUOTA_UNLIMITED = -1;
}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index c068e6a..53b49f0 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -280,6 +280,76 @@
* calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger
* a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
* focused.
+ *
+ * <h3>Web security</h3>
+ *
+ * <p>When handling autofill requests that represent web pages (typically
+ * view structures whose root's {@link android.app.assist.AssistStructure.ViewNode#getClassName()}
+ * is a {@link android.webkit.WebView}), the service should take the following steps to verify if
+ * the structure can be autofilled with the data associated with the app requesting it:
+ *
+ * <ol>
+ * <li>Use the {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()} to get the
+ * source of the document.
+ * <li>Get the canonical domain using the
+ * <a href="https://publicsuffix.org/>Public Suffix List</a> (see example below).
+ * <li>Use <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a>
+ * to obtain the package name and certificate fingerprint of the package corresponding to
+ * the canonical domain.
+ * <li>Make sure the certificate fingerprint matches the value returned by Package Manager
+ * (see "Package verification" section above).
+ * </ol>
+ *
+ * <p>Here's an example on how to get the canonical domain using
+ * <a href="https://github.com/google/guava">Guava</a>:
+ *
+ * <pre class="prettyprint">
+ * private static String getCanonicalDomain(String domain) {
+ * InternetDomainName idn = InternetDomainName.from(domain);
+ * while (!idn.isTopPrivateDomain() && idn != null) {
+ * idn = idn.parent();
+ * }
+ * return idn == null ? null : idn.toString();
+ * }
+ * </pre>
+ *
+ * <p>If the association between the web domain and app package cannot be verified through the steps
+ * above, the service can still autofill the app, but it should warn the user about the potential
+ * data leakage first, and askfor the user to confirm. For example, the service could:
+ *
+ * <ol>
+ * <li>Create a dataset that requires
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authentication} to
+ * unlock.
+ * <li>Include the web domain in the custom presentation for the
+ * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, android.widget.RemoteViews)
+ * dataset value}.
+ * <li>When the user select that dataset, show a disclaimer dialog explaining that the app is
+ * requesting credentials for a web domain, but the service could not verify if the app owns
+ * that domain. If the user agrees, then the service can unlock the dataset.
+ * <li>Similarly, when adding a {@link SaveInfo} object for the request, the service should
+ * include the above disclaimer in the {@link SaveInfo.Builder#setDescription(CharSequence)}.
+ * </ol>
+ *
+ * <p>This same procedure could also be used when the autofillable data is contained inside an
+ * {@code IFRAME}, in which case the WebView generates a new autofill context when a node inside
+ * the {@code IFRAME} is focused, which the root node containing the {@code IFRAME}'s {@code src}
+ * attribute on {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()}. A typical and
+ * legitimate use case for this scenario is a financial app that allows the user
+ * to login on different bank accounts. For example, a financial app {@code my_financial_app} could
+ * use a WebView that loads contents from {@code banklogin.my_financial_app.com}, which contains an
+ * {@code IFRAME} node whose {@code src} attribute is {@code login.some_bank.com}. When fulfilling
+ * that request, the service could add an
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authenticated dataset}
+ * whose presentation displays "Username for some_bank.com" and
+ * "Password for some_bank.com". Then when the user taps one of these options, the service
+ * shows the disclaimer dialog explaining that selecting that option would release the
+ * {@code login.some_bank.com} credentials to the {@code my_financial_app}; if the user agrees,
+ * then the service returns an unlocked dataset with the {@code some_bank.com} credentials.
+ *
+ * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
+ * verifications above, as long as the service can verify the authenticity of the browser app by
+ * checking its signing certificate.
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
@@ -424,7 +494,7 @@
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
- * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call
+ * <p><b>Note:</b> To retrieve the actual value of the field, the service should call
* {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls
* {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no
* guarantee such method will return the most recent value of the field.
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 3da0b5e..4f06bd7 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -22,11 +22,13 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
-import android.util.SparseArray;
+import android.util.Pair;
import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+
/**
* Defines a custom description for the Save UI affordance.
*
@@ -65,14 +67,17 @@
* new ImageTransformation.Builder(ccNumberId)
* .addOption(Pattern.compile(""^4815.*$"), R.drawable.ic_credit_card_logo1)
* .addOption(Pattern.compile(""^1623.*$"), R.drawable.ic_credit_card_logo2)
- * .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3);
+ * .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3)
+ * .build();
* // Masked credit card number (as .....LAST_4_DIGITS)
- * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation.Builder()
- * .addField(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1")
+ * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation
+ * .Builder(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1")
+ * .build();
* // Expiration date as MM / YYYY:
- * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation.Builder()
- * .addField(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1")
- * .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1");
+ * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation
+ * .Builder(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1")
+ * .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1")
+ * .build();
* </pre>
*
* <p>See {@link ImageTransformation}, {@link CharSequenceTransformation} for more info about these
@@ -83,7 +88,7 @@
private static final String TAG = "CustomDescription";
private final RemoteViews mPresentation;
- private final SparseArray<InternalTransformation> mTransformations;
+ private final ArrayList<Pair<Integer, InternalTransformation>> mTransformations;
private CustomDescription(Builder builder) {
mPresentation = builder.mPresentation;
@@ -96,8 +101,9 @@
final int size = mTransformations.size();
if (sDebug) Log.d(TAG, "getPresentation(): applying " + size + " transformations");
for (int i = 0; i < size; i++) {
- final int id = mTransformations.keyAt(i);
- final InternalTransformation transformation = mTransformations.valueAt(i);
+ final Pair<Integer, InternalTransformation> pair = mTransformations.get(i);
+ final int id = pair.first;
+ final InternalTransformation transformation = pair.second;
if (sDebug) Log.d(TAG, "#" + i + ": " + transformation);
try {
@@ -119,7 +125,7 @@
public static class Builder {
private final RemoteViews mPresentation;
- private SparseArray<InternalTransformation> mTransformations;
+ private ArrayList<Pair<Integer, InternalTransformation>> mTransformations;
/**
* Default constructor.
@@ -134,6 +140,9 @@
* Adds a transformation to replace the value of a child view with the fields in the
* screen.
*
+ * <p>When multiple transformations are added for the same child view, they will be applied
+ * in the same order as added.
+ *
* @param id view id of the children view.
* @param transformation an implementation provided by the Android System.
* @return this builder.
@@ -144,9 +153,9 @@
Preconditions.checkArgument((transformation instanceof InternalTransformation),
"not provided by Android System: " + transformation);
if (mTransformations == null) {
- mTransformations = new SparseArray<>();
+ mTransformations = new ArrayList<>();
}
- mTransformations.put(id, (InternalTransformation) transformation);
+ mTransformations.add(new Pair<>(id, (InternalTransformation) transformation));
return this;
}
@@ -189,8 +198,9 @@
final int[] ids = new int[size];
final InternalTransformation[] values = new InternalTransformation[size];
for (int i = 0; i < size; i++) {
- ids[i] = mTransformations.keyAt(i);
- values[i] = mTransformations.valueAt(i);
+ final Pair<Integer, InternalTransformation> pair = mTransformations.get(i);
+ ids[i] = pair.first;
+ values[i] = pair.second;
}
dest.writeIntArray(ids);
dest.writeParcelableArray(values, flags);
diff --git a/core/java/android/service/gatekeeper/GateKeeperResponse.java b/core/java/android/service/gatekeeper/GateKeeperResponse.java
index 287dc76..9b52934 100644
--- a/core/java/android/service/gatekeeper/GateKeeperResponse.java
+++ b/core/java/android/service/gatekeeper/GateKeeperResponse.java
@@ -106,6 +106,8 @@
if (mPayload != null) {
dest.writeInt(mPayload.length);
dest.writeByteArray(mPayload);
+ } else {
+ dest.writeInt(0);
}
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f548d3b..1c6275f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -195,7 +195,8 @@
// Needed for throttling onComputeColors.
private long mLastColorInvalidation;
private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
- private Supplier<Long> mClockFunction = SystemClock::elapsedRealtime;
+ private final Supplier<Long> mClockFunction;
+ private final Handler mHandler;
DisplayManager mDisplayManager;
Display mDisplay;
@@ -362,6 +363,26 @@
}
}
};
+
+ /**
+ * Default constructor
+ */
+ public Engine() {
+ this(SystemClock::elapsedRealtime, Handler.getMain());
+ }
+
+ /**
+ * Constructor used for test purposes.
+ *
+ * @param clockFunction Supplies current times in millis.
+ * @param handler Used for posting/deferring asynchronous calls.
+ * @hide
+ */
+ @VisibleForTesting
+ public Engine(Supplier<Long> clockFunction, Handler handler) {
+ mClockFunction = clockFunction;
+ mHandler = handler;
+ }
/**
* Provides access to the surface in which this wallpaper is drawn.
@@ -563,18 +584,17 @@
*/
public void notifyColorsChanged() {
final long now = mClockFunction.get();
- final Handler mainHandler = Handler.getMain();
if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) {
Log.w(TAG, "This call has been deferred. You should only call "
+ "notifyColorsChanged() once every "
+ (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds.");
- if (!mainHandler.hasCallbacks(mNotifyColorsChanged)) {
- mainHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS);
+ if (!mHandler.hasCallbacks(mNotifyColorsChanged)) {
+ mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS);
}
return;
}
mLastColorInvalidation = now;
- mainHandler.removeCallbacks(mNotifyColorsChanged);
+ mHandler.removeCallbacks(mNotifyColorsChanged);
try {
final WallpaperColors newColors = onComputeColors();
@@ -662,14 +682,6 @@
}
}
- /**
- * @hide
- */
- @VisibleForTesting
- public void setClockFunction(Supplier<Long> clockFunction) {
- mClockFunction = clockFunction;
- }
-
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
if (mDestroyed) {
Log.w(TAG, "Ignoring updateSurface: destroyed");
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 0cec496..c5c6b37 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -16,12 +16,18 @@
package android.view;
+import static android.view.DisplayInfoProto.APP_HEIGHT;
+import static android.view.DisplayInfoProto.APP_WIDTH;
+import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
+import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
+
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
import android.util.DisplayMetrics;
+import android.util.proto.ProtoOutputStream;
import libcore.util.Objects;
@@ -654,6 +660,22 @@
return sb.toString();
}
+ /**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.view.DisplayInfoProto}
+ *
+ * @param protoOutputStream Stream to write the Rect object to.
+ * @param fieldId Field Id of the DisplayInfoProto as defined in the parent message
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(LOGICAL_WIDTH, logicalWidth);
+ protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight);
+ protoOutputStream.write(APP_WIDTH, appWidth);
+ protoOutputStream.write(APP_HEIGHT, appHeight);
+ protoOutputStream.end(token);
+ }
+
private static String flagsToString(int flags) {
StringBuilder result = new StringBuilder();
if ((flags & Display.FLAG_SECURE) != 0) {
diff --git a/core/java/android/view/IApplicationToken.aidl b/core/java/android/view/IApplicationToken.aidl
index b01c0ef..a063a70 100644
--- a/core/java/android/view/IApplicationToken.aidl
+++ b/core/java/android/view/IApplicationToken.aidl
@@ -20,5 +20,6 @@
/** {@hide} */
interface IApplicationToken
{
+ String getName();
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1e50a85..fe50a64 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -34,6 +34,7 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2545,6 +2546,15 @@
}
/**
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(WindowLayoutParamsProto.TYPE, type);
+ proto.end(token);
+ }
+
+ /**
* Scale the layout params' coordinates and size.
* @hide
*/
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 49b7ed8..d2eb785 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -77,6 +77,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.animation.Animation;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -1646,6 +1647,14 @@
public void dump(String prefix, PrintWriter writer, String[] args);
/**
+ * Write the WindowManagerPolicy's state into the protocol buffer.
+ * The message is described in {@link com.android.server.wm.proto.WindowManagerPolicyProto}
+ *
+ * @param proto The protocol buffer output stream to write to.
+ */
+ void writeToProto(ProtoOutputStream proto, long fieldId);
+
+ /**
* Returns whether a given window type can be magnified.
*
* @param windowType The window type.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 29e5523..a0d9099 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1339,6 +1339,25 @@
}
/**
+ * Dismiss the Autofill UI, even if associated with other sessions.
+ *
+ * <p>Typically called when a new activity starts so it can hide the existing Save UI (if any).
+ *
+ * @hide
+ */
+ public void dismissUi() {
+ if (sVerbose) Log.v(TAG, "dismissUi()");
+
+ if (mService == null) return;
+
+ try {
+ mService.dismissUi();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* View tracking information. Once all tracked views become invisible the session is finished.
*/
private class TrackedViews {
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 627afa7..aa8e19a 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -49,4 +49,5 @@
void disableOwnedAutofillServices(int userId);
boolean isServiceSupported(int userId);
boolean isServiceEnabled(int userId, String packageName);
+ void dismissUi();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 650c7d9..049f1ef 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2725,6 +2725,18 @@
* understood by the {@link android.service.autofill.AutofillService} implementations:
*
* <ol>
+ * <li>Only the HTML nodes inside a {@code FORM} are generated.
+ * <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the
+ * node representing the WebView.
+ * <li>If a web page has multiple {@code FORM}s, only the data for the current form is
+ * represented—if the user taps a field from another form, then the current autofill
+ * context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and
+ * a new context is created for that {@code FORM}.
+ * <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in
+ * the view structure until the user taps a field from a {@code FORM} inside the
+ * {@code IFRAME}, in which case it would be treated the same way as multiple forms described
+ * above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the
+ * {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node.
* <li>If the Android SDK provides a similar View, then should be set with the
* fully-qualified class name of such view.
* <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to
diff --git a/core/proto/android/graphics/rect.proto b/core/proto/android/graphics/rect.proto
new file mode 100644
index 0000000..a65d331
--- /dev/null
+++ b/core/proto/android/graphics/rect.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.graphics;
+
+option java_multiple_files = true;
+
+message RectProto {
+ int32 left = 1;
+ int32 top = 2;
+ int32 right = 3;
+ int32 bottom = 4;
+}
+
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
new file mode 100644
index 0000000..7fb4802
--- /dev/null
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/view/displayinfo.proto";
+import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
+
+package com.android.server.wm.proto;
+
+option java_multiple_files = true;
+
+message WindowManagerServiceProto {
+ WindowManagerPolicyProto policy = 1;
+ /* window manager hierarchy structure */
+ repeated DisplayProto displays = 2;
+ /* window references in top down z order */
+ repeated IdentifierProto windows = 3;
+ IdentifierProto focused_window = 4;
+ string focused_app = 5;
+ IdentifierProto input_method_window = 6;
+ bool display_frozen = 7;
+ int32 rotation = 8;
+ int32 last_orientation = 9;
+ AppTransitionProto app_transition = 10;
+}
+
+/* represents PhoneWindowManager */
+message WindowManagerPolicyProto {
+ .android.graphics.RectProto stable_bounds = 1;
+}
+
+/* represents AppTransition */
+message AppTransitionProto {
+ enum AppState {
+ APP_STATE_IDLE = 0;
+ APP_STATE_READY = 1;
+ APP_STATE_RUNNING = 2;
+ APP_STATE_TIMEOUT = 3;
+ }
+ AppState app_transition_state = 1;
+ /* definitions for constants found in {@link com.android.server.wm.AppTransition} */
+ enum TransitionType {
+ TRANSIT_NONE = 0;
+ TRANSIT_UNSET = -1;
+ TRANSIT_ACTIVITY_OPEN = 6;
+ TRANSIT_ACTIVITY_CLOSE = 7;
+ TRANSIT_TASK_OPEN = 8;
+ TRANSIT_TASK_CLOSE = 9;
+ TRANSIT_TASK_TO_FRONT = 10;
+ TRANSIT_TASK_TO_BACK = 11;
+ TRANSIT_WALLPAPER_CLOSE = 12;
+ TRANSIT_WALLPAPER_OPEN = 13;
+ TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+ TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+ TRANSIT_TASK_OPEN_BEHIND = 16;
+ TRANSIT_TASK_IN_PLACE = 17;
+ TRANSIT_ACTIVITY_RELAUNCH = 18;
+ TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
+ TRANSIT_KEYGUARD_GOING_AWAY = 20;
+ TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
+ TRANSIT_KEYGUARD_OCCLUDE = 22;
+ TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+ }
+ TransitionType last_used_app_transition = 2;
+}
+
+/* represents DisplayContent */
+message DisplayProto {
+ int32 id = 1;
+ repeated StackProto stacks = 2;
+ DockedStackDividerControllerProto docked_stack_divider_controller = 3;
+ PinnedStackControllerProto pinned_stack_controller = 4;
+ /* non app windows */
+ repeated WindowTokenProto above_app_windows = 5;
+ repeated WindowTokenProto below_app_windows = 6;
+ repeated WindowTokenProto ime_windows = 7;
+ int32 dpi = 8;
+ .android.view.DisplayInfoProto display_info = 9;
+}
+
+
+/* represents DockedStackDividerController */
+message DockedStackDividerControllerProto {
+ bool minimized_dock = 1;
+}
+
+/* represents PinnedStackController */
+message PinnedStackControllerProto {
+ .android.graphics.RectProto default_bounds = 1;
+ .android.graphics.RectProto movement_bounds = 2;
+}
+
+/* represents TaskStack */
+message StackProto {
+ int32 id = 1;
+ repeated TaskProto tasks = 2;
+ bool fills_parent = 3;
+ .android.graphics.RectProto bounds = 4;
+ bool animation_background_surface_is_dimming = 5;
+}
+
+/* represents Task */
+message TaskProto {
+ int32 id = 1;
+ repeated AppWindowTokenProto app_window_tokens = 2;
+ bool fills_parent = 3;
+ .android.graphics.RectProto bounds = 4;
+ .android.graphics.RectProto temp_inset_bounds = 5;
+}
+
+/* represents AppWindowToken */
+message AppWindowTokenProto {
+ /* obtained from ActivityRecord */
+ string name = 1;
+ WindowTokenProto window_token = 2;
+}
+
+/* represents WindowToken */
+message WindowTokenProto {
+ int32 hash_code = 1;
+ repeated WindowStateProto windows = 2;
+}
+
+/* represents WindowState */
+message WindowStateProto {
+ IdentifierProto identifier = 1;
+ int32 display_id = 2;
+ int32 stack_id = 3;
+ .android.view.WindowLayoutParamsProto attributes = 4;
+ .android.graphics.RectProto given_content_insets = 5;
+ .android.graphics.RectProto frame = 6;
+ .android.graphics.RectProto containing_frame = 7;
+ .android.graphics.RectProto parent_frame = 8;
+ .android.graphics.RectProto content_frame = 9;
+ .android.graphics.RectProto content_insets = 10;
+ .android.graphics.RectProto surface_insets = 11;
+ WindowStateAnimatorProto animator = 12;
+ bool animating_exit = 13;
+ repeated WindowStateProto child_windows = 14;
+}
+
+message IdentifierProto {
+ int32 hash_code = 1;
+ int32 user_id = 2;
+ string title = 3;
+}
+
+/* represents WindowStateAnimator */
+message WindowStateAnimatorProto {
+ .android.graphics.RectProto last_clip_rect = 1;
+ WindowSurfaceControllerProto surface = 2;
+}
+
+/* represents WindowSurfaceController */
+message WindowSurfaceControllerProto {
+ bool shown = 1;
+ int32 layer = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto
new file mode 100644
index 0000000..8583868
--- /dev/null
+++ b/core/proto/android/view/displayinfo.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.view;
+
+option java_multiple_files = true;
+
+/* represents DisplayInfo */
+message DisplayInfoProto {
+ int32 logical_width = 1;
+ int32 logical_height = 2;
+ int32 app_width = 3;
+ int32 app_height = 4;
+}
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
new file mode 100644
index 0000000..5bb84dc
--- /dev/null
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.view;
+
+option java_multiple_files = true;
+
+/* represents WindowManager.LayoutParams */
+message WindowLayoutParamsProto {
+ int32 type = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 400c9e8..361aa08 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -775,7 +775,7 @@
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
- android:protectionLevel="dangerous|ephemeral" />
+ android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access approximate location.
Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
@@ -785,7 +785,7 @@
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
- android:protectionLevel="dangerous|ephemeral" />
+ android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -820,13 +820,13 @@
android:protectionLevel="dangerous" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
- granted by {@link #READ_PHONE_STATE} but is exposed to ephemeral applications.
+ granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
<p>Protection level: dangerous-->
<permission android:name="android.permission.READ_PHONE_NUMBERS"
android:permissionGroup="android.permission-group.PHONE"
android:label="@string/permlab_readPhoneNumbers"
android:description="@string/permdesc_readPhoneNumbers"
- android:protectionLevel="dangerous|ephemeral" />
+ android:protectionLevel="dangerous|instant" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
@@ -1008,7 +1008,7 @@
android:permissionGroup="android.permission-group.CAMERA"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous|ephemeral" />
+ android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
@@ -1251,7 +1251,7 @@
<permission android:name="android.permission.INTERNET"
android:description="@string/permdesc_createNetworkSockets"
android:label="@string/permlab_createNetworkSockets"
- android:protectionLevel="normal|ephemeral" />
+ android:protectionLevel="normal|instant" />
<!-- Allows applications to access information about networks.
<p>Protection level: normal
@@ -1259,7 +1259,7 @@
<permission android:name="android.permission.ACCESS_NETWORK_STATE"
android:description="@string/permdesc_accessNetworkState"
android:label="@string/permlab_accessNetworkState"
- android:protectionLevel="normal|ephemeral" />
+ android:protectionLevel="normal|instant" />
<!-- Allows applications to access information about Wi-Fi networks.
<p>Protection level: normal
@@ -1482,7 +1482,7 @@
<permission android:name="android.permission.VIBRATE"
android:label="@string/permlab_vibrate"
android:description="@string/permdesc_vibrate"
- android:protectionLevel="normal|ephemeral" />
+ android:protectionLevel="normal|instant" />
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
from dimming.
@@ -1491,7 +1491,7 @@
<permission android:name="android.permission.WAKE_LOCK"
android:label="@string/permlab_wakeLock"
android:description="@string/permdesc_wakeLock"
- android:protectionLevel="normal|ephemeral" />
+ android:protectionLevel="normal|instant" />
<!-- Allows using the device's IR transmitter, if available.
<p>Protection level: normal
@@ -2390,7 +2390,8 @@
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi @hide Allows an application to collect battery statistics -->
+ <!-- @SystemApi @hide Allows an application to collect application operation statistics.
+ Not for use by third party apps. -->
<permission android:name="android.permission.GET_APP_OPS_STATS"
android:protectionLevel="signature|privileged|development" />
@@ -3541,7 +3542,7 @@
<!-- Allows an instant app to create foreground services. -->
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
- android:protectionLevel="signature|development|ephemeral|appop" />
+ android:protectionLevel="signature|development|instant|appop" />
<application android:process="system"
android:persistent="true"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 07a9a33..694a98b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -242,9 +242,9 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the setup wizard app -->
<flag name="setup" value="0x800" />
- <!-- Additional flag from base permission type: this permission can be granted to ephemeral
+ <!-- Additional flag from base permission type: this permission can be granted to instant
apps -->
- <flag name="ephemeral" value="0x1000" />
+ <flag name="instant" value="0x1000" />
<!-- Additional flag from base permission type: this permission can only be granted to apps
that target runtime permissions ({@link android.os.Build.VERSION_CODES#M} and above)
-->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 95aa264..f19a086 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2197,10 +2197,14 @@
<string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
- <!-- Name of the CustomDialog that is used for VPN -->
- <string name="config_customVpnConfirmDialogComponent"
+ <!-- Name of the dialog that is used to request the user's consent to VPN connection -->
+ <string name="config_customVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
+ <!-- Name of the dialog that is used to inform the user that always-on VPN is disconnected -->
+ <string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false"
+ >com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string>
+
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0ff24b7..4bec944 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3444,16 +3444,21 @@
<!-- The text of the notification when VPN is active with a session name. -->
<string name="vpn_text_long">Connected to <xliff:g id="session" example="office">%s</xliff:g>. Tap to manage the network.</string>
- <!-- Notification title when connecting to lockdown VPN. -->
+ <!-- Notification title when connecting to always-on VPN, a VPN that's set to always stay
+ connected. -->
<string name="vpn_lockdown_connecting">Always-on VPN connecting\u2026</string>
- <!-- Notification title when connected to lockdown VPN. -->
+ <!-- Notification title when connected to always-on VPN, a VPN that's set to always stay
+ connected. -->
<string name="vpn_lockdown_connected">Always-on VPN connected</string>
- <!-- Notification title when not connected to lockdown VPN. -->
- <string name="vpn_lockdown_disconnected">Always-on VPN disconnected</string>
- <!-- Notification title when error connecting to lockdown VPN. -->
+ <!-- Notification title when not connected to always-on VPN, a VPN that's set to always stay
+ connected. -->
+ <string name="vpn_lockdown_disconnected">Disconnected from always-on VPN</string>
+ <!-- Notification title when error connecting to always-on VPN, a VPN that's set to always stay
+ connected. -->
<string name="vpn_lockdown_error">Always-on VPN error</string>
- <!-- Notification body that indicates user can touch to configure lockdown VPN connection. -->
- <string name="vpn_lockdown_config">Tap to set up</string>
+ <!-- Notification body that indicates user can touch to configure always-on VPN, a VPN that's
+ set to always stay connected. -->
+ <string name="vpn_lockdown_config">Change network or VPN settings</string>
<!-- Localized strings for WebView -->
<!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 326d31a..0fed465 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2002,6 +2002,7 @@
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
+ <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="string" name="config_persistentDataPackageName" />
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index deafb66..3dc928d 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -21,6 +21,7 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -194,7 +195,24 @@
pw.print(top); pw.print("]["); pw.print(right);
pw.print(','); pw.print(bottom); pw.print(']');
}
-
+
+ /**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.graphics.RectProto}
+ *
+ * @param protoOutputStream Stream to write the Rect object to.
+ * @param fieldId Field Id of the Rect as defined in the parent message
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(RectProto.LEFT, left);
+ protoOutputStream.write(RectProto.TOP, top);
+ protoOutputStream.write(RectProto.RIGHT, right);
+ protoOutputStream.write(RectProto.BOTTOM, bottom);
+ protoOutputStream.end(token);
+ }
+
/**
* Returns true if the rectangle is empty (left >= right or top >= bottom)
*/
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index bc41810..8b03468 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -99,13 +99,22 @@
}
auto deviceInfo = DeviceInfo::get();
+ auto displayInfo = deviceInfo->displayInfo();
int maxTextureSize = deviceInfo->maxTextureSize();
+ // Adjust cache size based on Pixel's desnsity.
+ constexpr float PIXEL_DENSITY = 2.6;
+ const float densityRatio = displayInfo.density / PIXEL_DENSITY;
+
// TODO: Most devices are hardcoded with this configuration, does it need to be dynamic?
- mSmallCacheWidth = std::min(1024, maxTextureSize);
- mSmallCacheHeight = std::min(1024, maxTextureSize);
- mLargeCacheWidth = std::min(2048, maxTextureSize);
- mLargeCacheHeight = std::min(1024, maxTextureSize);
+ mSmallCacheWidth =
+ OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
+ mSmallCacheHeight =
+ OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
+ mLargeCacheWidth =
+ OffscreenBuffer::computeIdealDimension(std::min(2048, maxTextureSize) * densityRatio);
+ mLargeCacheHeight =
+ OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
if (sLogFontRendererCreate) {
INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 4c84ad2..acc7539 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -176,7 +176,7 @@
return sRenderPipelineType;
}
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_RENDERER, prop, "skiagl");
+ property_get(PROPERTY_RENDERER, prop, "opengl");
if (!strcmp(prop, "skiagl") ) {
ALOGD("Skia GL Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaGL;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index bf18a7d..f85925d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2095,6 +2095,7 @@
errors |= ERROR_UNRECOGNIZED;
}
switch (profileLevel.profile) {
+ case CodecProfileLevel.AVCProfileConstrainedHigh:
case CodecProfileLevel.AVCProfileHigh:
BR *= 1250; break;
case CodecProfileLevel.AVCProfileHigh10:
@@ -2107,6 +2108,7 @@
errors |= ERROR_UNSUPPORTED;
supported = false;
// fall through - treat as base profile
+ case CodecProfileLevel.AVCProfileConstrainedBaseline:
case CodecProfileLevel.AVCProfileBaseline:
case CodecProfileLevel.AVCProfileMain:
BR *= 1000; break;
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 48fb5bf..0f46096 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -703,37 +703,37 @@
}
/**
- * Returns {@code true}, if {@code uri} is a channel URI.
+ * @return {@code true} if {@code uri} is a channel URI.
*/
- public static boolean isChannelUri(Uri uri) {
+ public static boolean isChannelUri(@NonNull Uri uri) {
return isChannelUriForTunerInput(uri) || isChannelUriForPassthroughInput(uri);
}
/**
- * Returns {@code true}, if {@code uri} is a channel URI for a tuner input.
+ * @return {@code true} if {@code uri} is a channel URI for a tuner input.
*/
- public static boolean isChannelUriForTunerInput(Uri uri) {
+ public static boolean isChannelUriForTunerInput(@NonNull Uri uri) {
return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_CHANNEL);
}
/**
- * Returns {@code true}, if {@code uri} is a channel URI for a pass-through input.
+ * @return {@code true} if {@code uri} is a channel URI for a pass-through input.
*/
- public static boolean isChannelUriForPassthroughInput(Uri uri) {
+ public static boolean isChannelUriForPassthroughInput(@NonNull Uri uri) {
return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PASSTHROUGH);
}
/**
- * Returns {@code true}, if {@code uri} is a program URI.
+ * @return {@code true} if {@code uri} is a program URI.
*/
- public static boolean isProgramUri(Uri uri) {
+ public static boolean isProgramUri(@NonNull Uri uri) {
return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_PROGRAM);
}
/**
- * Returns {@code true}, if {@code uri} is a recorded program URI.
+ * @return {@code true} if {@code uri} is a recorded program URI.
*/
- public static boolean isRecordedProgramUri(Uri uri) {
+ public static boolean isRecordedProgramUri(@NonNull Uri uri) {
return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_RECORDED_PROGRAM);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index ad46b60..d592c30 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -486,6 +486,12 @@
loadingContent.layout(0, 0, loadingContent.getMeasuredWidth(),
loadingContent.getMeasuredHeight());
+ // To create a bitmap, height & width should be larger than 0
+ if (mPageContentHeight <= 0 || mPageContentWidth <= 0) {
+ Log.w(LOG_TAG, "Unable to create bitmap, height or width smaller than 0!");
+ return;
+ }
+
Bitmap loadingBitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
Bitmap.Config.ARGB_8888);
loadingContent.draw(new Canvas(loadingBitmap));
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index db3274a..1f1b67e 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -121,8 +121,8 @@
<item>Use System Selection (Default)</item>
<item>SBC</item>
<item>AAC</item>
- <item>aptX</item>
- <item>aptX HD</item>
+ <item><xliff:g id="aptx">Qualcomm(R) aptX(TM) audio</xliff:g></item>
+ <item><xliff:g id="aptx_hd">Qualcomm(R) aptX(TM) HD audio</xliff:g></item>
<item>LDAC</item>
<item>Enable Optional Codecs</item>
<item>Disable Optional Codecs</item>
@@ -145,8 +145,8 @@
<item>Use System Selection (Default)</item>
<item>SBC</item>
<item>AAC</item>
- <item>aptX</item>
- <item>aptX HD</item>
+ <item><xliff:g id="aptx">Qualcomm(R) aptX(TM) audio</xliff:g></item>
+ <item><xliff:g id="aptx_hd">Qualcomm(R) aptX(TM) HD audio</xliff:g></item>
<item>LDAC</item>
<item>Enable Optional Codecs</item>
<item>Disable Optional Codecs</item>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
index 5243228..1285ed8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -27,7 +27,7 @@
public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_BUTTON";
- public static final int VERSION = 2;
+ public static final int VERSION = 3;
/**
* Returns a view in the nav bar. If the id is set "back", "home", "recent_apps", "menu",
@@ -46,8 +46,6 @@
void setVertical(boolean vertical);
- void setCarMode(boolean carMode);
-
void setDarkIntensity(float intensity);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 669594b..bb6213b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1157,10 +1157,16 @@
}
}
+ private boolean shouldListenForFingerprintAssistant() {
+ return mAssistantVisible && mKeyguardOccluded
+ && !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
+ && !mUserHasTrust.get(getCurrentUser(), false);
+ }
+
private boolean shouldListenForFingerprint() {
return (mKeyguardIsVisible || !mDeviceInteractive ||
(mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
- (mAssistantVisible && mKeyguardOccluded))
+ shouldListenForFingerprintAssistant())
&& !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
&& !mKeyguardGoingAway;
}
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
index 9fe730a..81e4db3 100644
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
@@ -33,6 +33,7 @@
private static final String TAG = "DockedStackExistsListener";
private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
+ private static boolean mLastExists;
static {
try {
@@ -73,6 +74,7 @@
private static void onDockedStackExistsChanged(boolean exists) {
+ mLastExists = exists;
synchronized (sCallbacks) {
sCallbacks.removeIf(wf -> {
Consumer<Boolean> l = wf.get();
@@ -83,6 +85,7 @@
}
public static void register(Consumer<Boolean> callback) {
+ callback.accept(mLastExists);
synchronized (sCallbacks) {
sCallbacks.add(new WeakReference<>(callback));
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 9b48320..56a3c87 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -31,7 +31,6 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
@@ -47,10 +46,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.PipSnapAlgorithm;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter;
@@ -90,6 +87,8 @@
// The current movement bounds
private Rect mMovementBounds = new Rect();
+ // The reference inset bounds, used to determine the dismiss fraction
+ private Rect mInsetBounds = new Rect();
// The reference bounds used to calculate the normal/expanded target bounds
private Rect mNormalBounds = new Rect();
private Rect mNormalMovementBounds = new Rect();
@@ -311,6 +310,7 @@
mNormalMovementBounds = normalMovementBounds;
mExpandedMovementBounds = expandedMovementBounds;
mDisplayRotation = displayRotation;
+ mInsetBounds.set(insetBounds);
updateMovementBounds(mMenuState);
// If we have a deferred resize, apply it now
@@ -418,9 +418,12 @@
* Updates the appearance of the menu and scrim on top of the PiP while dismissing.
*/
private void updateDismissFraction() {
- if (mMenuController != null) {
+ // Skip updating the dismiss fraction when the IME is showing. This is to work around an
+ // issue where starting the menu activity for the dismiss overlay will steal the window
+ // focus, which closes the IME.
+ if (mMenuController != null && !mIsImeShowing) {
Rect bounds = mMotionHelper.getBounds();
- final float target = mMovementBounds.bottom + bounds.height();
+ final float target = mInsetBounds.bottom;
float fraction = 0f;
if (bounds.bottom > target) {
final float distance = bounds.bottom - target;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 585b151..562210c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -69,6 +69,7 @@
import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
@@ -517,6 +518,11 @@
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+
+ // Workaround for b/64694148: The animation started callback is not made (see
+ // RecentsImpl.getThumbnailTransitionActivityOptions) so reset the transition-waiting state
+ // once the enter animation has completed.
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 2bc2665..a83e659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -174,16 +174,6 @@
mCurrentView = currentView.findViewById(mId);
}
- public void setCarMode(boolean carMode) {
- final int N = mViews.size();
- for (int i = 0; i < N; i++) {
- final View view = mViews.get(i);
- if (view instanceof ButtonInterface) {
- ((ButtonInterface) view).setCarMode(carMode);
- }
- }
- }
-
public void setVertical(boolean vertical) {
mVertical = vertical;
final int N = mViews.size();
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 57c8827..9a7039a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -683,8 +683,6 @@
if (isCarMode != mInCarMode) {
mInCarMode = isCarMode;
- getHomeButton().setCarMode(isCarMode);
-
if (ALTERNATE_CAR_MODE_UI) {
mUseCarModeUi = isCarMode;
uiCarModeChanged = true;
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 59e7005..65bfb49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -429,7 +429,7 @@
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected StatusBarWindowManager mStatusBarWindowManager;
protected UnlockMethodCache mUnlockMethodCache;
- private DozeServiceHost mDozeServiceHost;
+ private DozeServiceHost mDozeServiceHost = new DozeServiceHost();
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
@@ -733,7 +733,8 @@
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private UserSwitcherController mUserSwitcherController;
private NetworkController mNetworkController;
- private KeyguardMonitorImpl mKeyguardMonitor;
+ private KeyguardMonitorImpl mKeyguardMonitor
+ = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
private BatteryController mBatteryController;
protected boolean mPanelExpanded;
private IOverlayManager mOverlayManager;
@@ -741,7 +742,7 @@
private boolean mIsKeyguard;
private LogMaker mStatusBarStateLog;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
- private NotificationIconAreaController mNotificationIconAreaController;
+ protected NotificationIconAreaController mNotificationIconAreaController;
private boolean mReinflateNotificationsOnUserSwitched;
private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
private boolean mClearAllEnabled;
@@ -788,14 +789,12 @@
public void start() {
mNetworkController = Dependency.get(NetworkController.class);
mUserSwitcherController = Dependency.get(UserSwitcherController.class);
- mKeyguardMonitor = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mBatteryController = Dependency.get(BatteryController.class);
mAssistManager = Dependency.get(AssistManager.class);
- mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
@@ -981,7 +980,6 @@
startKeyguard();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
- mDozeServiceHost = new DozeServiceHost();
putComponent(DozeHost.class, mDozeServiceHost);
notifyUserAboutHiddenNotifications();
@@ -1328,7 +1326,7 @@
// Clock and bottom icons
mNotificationPanel.onOverlayChanged();
// The status bar on the keyguard is a special layout.
- mKeyguardStatusBar.onOverlayChanged();
+ if (mKeyguardStatusBar != null) mKeyguardStatusBar.onOverlayChanged();
// Recreate Indication controller because internal references changed
mKeyguardIndicationController =
SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
@@ -1373,7 +1371,7 @@
}
private void inflateSignalClusters() {
- reinflateSignalCluster(mKeyguardStatusBar);
+ if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar);
}
public static SignalClusterView reinflateSignalCluster(View view) {
@@ -4565,7 +4563,7 @@
if (mKeyguardUserSwitcher != null) {
mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
}
- mStatusBarView.removePendingHideExpandedRunnables();
+ if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables();
if (mAmbientIndicationContainer != null) {
mAmbientIndicationContainer.setVisibility(View.VISIBLE);
}
@@ -4603,7 +4601,7 @@
/**
* Switches theme from light to dark and vice-versa.
*/
- private void updateTheme() {
+ protected void updateTheme() {
final boolean inflated = mStackScroller != null;
// The system wallpaper defines if QS should be light or dark.
@@ -5673,7 +5671,8 @@
protected KeyguardManager mKeyguardManager;
private LockPatternUtils mLockPatternUtils;
- private DeviceProvisionedController mDeviceProvisionedController;
+ private DeviceProvisionedController mDeviceProvisionedController
+ = Dependency.get(DeviceProvisionedController.class);
protected SystemServicesProxy mSystemServicesProxy;
// UI-specific methods
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 65bfabd..0501771 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -304,11 +304,6 @@
public void setVertical(boolean vertical) {
//no op
}
-
- @Override
- public void setCarMode(boolean carMode) {
- // no op
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java b/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
index 5dbcd8a..360907b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
@@ -46,9 +46,16 @@
throw new IllegalStateException("Expected matching children");
}
boolean hasChanges = false;
+ View lastView = null;
for (int i = 0; i < size; i++) {
View radio = radioGroup.getChildAt(i);
View content = radioContent.getChildAt(i);
+ if (lastView != null) {
+ radio.setAccessibilityTraversalAfter(lastView.getId());
+ }
+ View contentClick = findFirstClickable(content);
+ if (contentClick != null) contentClick.setAccessibilityTraversalAfter(radio.getId());
+ lastView = findLastClickable(content);
if (radio.getLayoutParams().height != content.getMeasuredHeight()) {
hasChanges = true;
radio.getLayoutParams().height = content.getMeasuredHeight();
@@ -59,4 +66,28 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
+
+ private View findFirstClickable(View content) {
+ if (content.isClickable()) return content;
+ if (content instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) content;
+ for (int i = 0; i < group.getChildCount(); i++) {
+ View v = findFirstClickable(group.getChildAt(i));
+ if (v != null) return v;
+ }
+ }
+ return null;
+ }
+
+ private View findLastClickable(View content) {
+ if (content.isClickable()) return content;
+ if (content instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) content;
+ for (int i = group.getChildCount() - 1; i >= 0; i--) {
+ View v = findLastClickable(group.getChildAt(i));
+ if (v != null) return v;
+ }
+ }
+ return null;
+ }
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index adb3baf..67fae5b 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -42,6 +42,7 @@
<uses-permission android:name="android.permission.TRUST_LISTENER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
<application>
<uses-library android:name="android.test.runner" />
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 d0f0bfd..6417eb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -17,6 +17,7 @@
package com.android.systemui.colorextraction;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
@@ -25,7 +26,6 @@
import android.support.test.runner.AndroidJUnit4;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.types.Tonal;
import com.android.systemui.SysuiTestCase;
import org.junit.Test;
@@ -48,18 +48,21 @@
@Test
public void getColors_usesGreyIfWallpaperNotVisible() {
- SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(),
- new Tonal(getContext()), false);
+ ColorExtractor.GradientColors colors = new ColorExtractor.GradientColors();
+ colors.setMainColor(Color.RED);
+ colors.setSecondaryColor(Color.RED);
+
+ SysuiColorExtractor extractor = getTestableExtractor(colors);
simulateEvent(extractor);
extractor.setWallpaperVisible(false);
ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
- for (int which : sWhich) {
- for (int type : sTypes) {
- assertEquals("Not using fallback!", extractor.getColors(which, type),
- fallbackColors);
- }
+ for (int type : sTypes) {
+ assertEquals("Not using fallback!",
+ extractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
+ assertNotEquals("Wallpaper visibility event should not affect lock wallpaper.",
+ extractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
}
}
@@ -69,13 +72,7 @@
colors.setMainColor(Color.RED);
colors.setSecondaryColor(Color.RED);
- SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(),
- (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
- outGradientColorsExtraDark) -> {
- outGradientColorsNormal.set(colors);
- outGradientColorsDark.set(colors);
- outGradientColorsExtraDark.set(colors);
- }, false);
+ SysuiColorExtractor extractor = getTestableExtractor(colors);
simulateEvent(extractor);
extractor.setWallpaperVisible(true);
@@ -87,6 +84,16 @@
}
}
+ private SysuiColorExtractor getTestableExtractor(ColorExtractor.GradientColors colors) {
+ return new SysuiColorExtractor(getContext(),
+ (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
+ outGradientColorsExtraDark) -> {
+ outGradientColorsNormal.set(colors);
+ outGradientColorsDark.set(colors);
+ outGradientColorsExtraDark.set(colors);
+ }, false);
+ }
+
private void simulateEvent(SysuiColorExtractor extractor) {
// Let's fake a color event
extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), null, null, 0),
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 a706368..ac367d2 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
@@ -26,21 +26,19 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.trust.TrustManager;
+import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
+import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IPowerManager;
@@ -51,20 +49,25 @@
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.support.test.metricshelper.MetricsAsserts;
-import android.support.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
+import android.testing.LayoutInflaterBuilder;
import android.testing.TestableLooper;
import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
+import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -73,7 +76,11 @@
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import org.junit.Before;
@@ -106,12 +113,20 @@
@Before
public void setup() throws Exception {
+ mContext.setTheme(R.style.Theme_SystemUI_Light);
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(DeviceProvisionedController.class);
+ mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
+ CommandQueue commandQueue = mock(CommandQueue.class);
+ when(commandQueue.asBinder()).thenReturn(new Binder());
+ mContext.putComponent(CommandQueue.class, commandQueue);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
mStatusBarKeyguardViewManager = mock(StatusBarKeyguardViewManager.class);
mUnlockMethodCache = mock(UnlockMethodCache.class);
mKeyguardIndicationController = mock(KeyguardIndicationController.class);
mStackScroller = mock(NotificationStackScrollLayout.class);
+ when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
mMetricsLogger = new FakeMetricsLogger();
mHeadsUpManager = mock(HeadsUpManager.class);
mNotificationData = mock(NotificationData.class);
@@ -133,6 +148,7 @@
mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
mBarService);
mStatusBar.mContext = mContext;
+ mStatusBar.mComponents = mContext.getComponents();
doAnswer(invocation -> {
OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
onDismissAction.onDismiss();
@@ -484,6 +500,16 @@
mStatusBar.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
}
+ @Test
+ @RunWithLooper(setAsMainLooper = true)
+ public void testUpdateKeyguardState_DoesNotCrash() {
+ mStatusBar.mStatusBarWindow = mock(StatusBarWindowView.class);
+ mStatusBar.mState = StatusBarState.KEYGUARD;
+ mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
+ mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
+ mStatusBar.updateKeyguardState(false, false);
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -502,6 +528,7 @@
mNotificationPanel = panelView;
mBarService = barService;
mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
+ mScrimController = mock(ScrimController.class);
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
@@ -511,6 +538,11 @@
return wakefulnessLifecycle;
}
+ @Override
+ protected void updateTheme() {
+ // Do nothing for now, until we have more mocking and StatusBar is smaller.
+ }
+
public void setBarStateForTest(int state) {
mState = state;
}
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index a3d27ce..8172e71 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -23,9 +23,10 @@
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<application android:label="VpnDialogs"
- android:allowBackup="false" >
+ android:allowBackup="false">
+
<activity android:name=".ConfirmDialog"
- android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -33,12 +34,21 @@
</activity>
<activity android:name=".ManageDialog"
- android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
- android:noHistory="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:permission="android.permission.NETWORK_SETTINGS"
+ android:exported="true">
</activity>
+
+ <activity android:name=".AlwaysOnDisconnectedDialog"
+ android:label="@string/always_on_disconnected_title"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:permission="android.permission.NETWORK_SETTINGS"
+ android:exported="true">
+ </activity>
+
</application>
</manifest>
diff --git a/packages/VpnDialogs/res/layout/always_on_disconnected.xml b/packages/VpnDialogs/res/layout/always_on_disconnected.xml
new file mode 100644
index 0000000..0f4a46d
--- /dev/null
+++ b/packages/VpnDialogs/res/layout/always_on_disconnected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="24dp">
+ <TextView android:id="@+id/message"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+</ScrollView>
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 406bcc3..443a9bc 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -18,7 +18,6 @@
<!-- Dialog title to identify the request from a VPN application. [CHAR LIMIT=60] -->
<string name="prompt">Connection request</string>
-
<!-- Dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] -->
<string name="warning"><xliff:g id="app">%s</xliff:g> wants to set up a VPN connection
that allows it to monitor network traffic. Only accept if you trust the source.
@@ -31,11 +30,6 @@
<!-- Dialog title for built-in VPN. [CHAR LIMIT=40] -->
<string name="legacy_title">VPN is connected</string>
- <!-- Button label to configure the current VPN session. [CHAR LIMIT=20] -->
- <string name="configure">Configure</string>
- <!-- Button label to disconnect the current VPN session. [CHAR LIMIT=20] -->
- <string name="disconnect">Disconnect</string>
-
<!-- Label for the name of the current VPN session. [CHAR LIMIT=20] -->
<string name="session">Session:</string>
<!-- Label for the duration of the current VPN session. [CHAR LIMIT=20] -->
@@ -44,10 +38,55 @@
<string name="data_transmitted">Sent:</string>
<!-- Label for the network usage of data received over VPN. [CHAR LIMIT=20] -->
<string name="data_received">Received:</string>
-
<!-- Formatted string for the network usage over VPN. [CHAR LIMIT=40] -->
<string name="data_value_format">
<xliff:g id="number">%1$s</xliff:g> bytes /
<xliff:g id="number">%2$s</xliff:g> packets
</string>
+
+ <!-- This string is the title of a dialog. The dialog shows up for Android users when always-on
+ VPN, a VPN that's set to always stay connected, loses its connection. [CHAR LIMIT=60] -->
+ <string name="always_on_disconnected_title">Can\'t connect to always-on VPN</string>
+ <!-- This message is part of a dialog. The dialog shows up for users when always-on VPN, a VPN
+ that's set to always stay connected, loses its connection. Until the phone can reconnect to
+ the VPN, it'll automatically connect to a public network if possible. This text is followed
+ by a clickable link that leads to VPN settings. [CHAR LIMIT=NONE] -->
+ <string name="always_on_disconnected_message">
+ <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> is set up to stay connected all
+ the time, but it can\'t connect right now. Your phone will use a public network until it can
+ reconnect to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>.
+ </string>
+ <!-- This message is part of a dialog. The dialog shows up for users when always-on VPN, a VPN
+ that's set to always stay connected, loses its connection while in the lockdown mode.
+ Until the phone can reconnect to the VPN, it won't be able to connect to the Internet. This
+ text is followed by a clickable link that leads to VPN settings. [CHAR LIMIT=NONE] -->
+ <string name="always_on_disconnected_message_lockdown">
+ <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> is set up to stay connected all
+ the time, but it can\'t connect right now. You won\'t have a connection until the VPN can
+ reconnect.
+ </string>
+ <!-- This is a space separating the body text and the "Change VPN settings" link that follows
+ it. [CHAR LIMIT=5] -->
+ <string name="always_on_disconnected_message_separator">" "</string>
+ <!-- This is a clickable link appended at the end of the body text of a dialog. The dialog shows
+ up for users when always-on VPN, a VPN that's set to always stay connected, loses its
+ connection. This link takes the user to the VPN page in Settings. -->
+ <string name="always_on_disconnected_message_settings_link">Change VPN settings</string>
+
+ <!-- This is the label of a button in a dialog. The button takes the user to the VPN settings
+ screen. [CHAR LIMIT=20] -->
+ <string name="configure">Configure</string>
+ <!-- This is the label of a button in a dialog. The button lets the user disconnect from the
+ current VPN connection. [CHAR LIMIT=20] -->
+ <string name="disconnect">Disconnect</string>
+ <!-- This button is part of a dialog, and it opens the user's VPN app. The dialog shows up for
+ users when always-on VPN, a VPN that's set to always stay connected, loses its connection.
+ Until the phone can reconnect to the VPN, it may automatically connect to a public network.
+ If it doesn't, the user won't have a connection until the VPN reconnects. [CHAR LIMIT=20]
+ -->
+ <string name="open_app">Open app</string>
+ <!-- This is the label of a button in a dialog. The button lets the user dismiss the dialog
+ without any consequences. [CHAR LIMIT=20] -->
+ <string name="dismiss">Dismiss</string>
+
</resources>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
new file mode 100644
index 0000000..846fcf8
--- /dev/null
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -0,0 +1,139 @@
+/*
+ * 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.vpndialogs;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.net.VpnConfig;
+
+public class AlwaysOnDisconnectedDialog extends AlertActivity
+ implements DialogInterface.OnClickListener{
+
+ private static final String TAG = "VpnDisconnected";
+
+ private IConnectivityManager mService;
+ private int mUserId;
+ private String mVpnPackage;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mUserId = UserHandle.myUserId();
+ mVpnPackage = getAlwaysOnVpnPackage();
+ if (mVpnPackage == null) {
+ finish();
+ return;
+ }
+
+ View view = View.inflate(this, R.layout.always_on_disconnected, null);
+ TextView messageView = view.findViewById(R.id.message);
+ messageView.setText(getMessage(getIntent().getBooleanExtra("lockdown", false)));
+ messageView.setMovementMethod(LinkMovementMethod.getInstance());
+
+ mAlertParams.mTitle = getString(R.string.always_on_disconnected_title);
+ mAlertParams.mPositiveButtonText = getString(R.string.open_app);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText = getString(R.string.dismiss);
+ mAlertParams.mNegativeButtonListener = this;
+ mAlertParams.mCancelable = false;
+ mAlertParams.mView = view;
+ setupAlert();
+
+ getWindow().setCloseOnTouchOutside(false);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case BUTTON_POSITIVE:
+ PackageManager pm = getPackageManager();
+ final Intent intent = pm.getLaunchIntentForPackage(mVpnPackage);
+ if (intent != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ }
+ finish();
+ break;
+ case BUTTON_NEGATIVE:
+ finish();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private String getAlwaysOnVpnPackage() {
+ try {
+ return mService.getAlwaysOnVpnPackage(mUserId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
+ return null;
+ }
+ }
+
+ private CharSequence getVpnLabel() {
+ try {
+ return VpnConfig.getVpnLabel(this, mVpnPackage);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Can't getVpnLabel() for " + mVpnPackage, e);
+ return mVpnPackage;
+ }
+ }
+
+ private CharSequence getMessage(boolean isLockdown) {
+ final SpannableStringBuilder message = new SpannableStringBuilder();
+ final int baseMessageResId = isLockdown
+ ? R.string.always_on_disconnected_message_lockdown
+ : R.string.always_on_disconnected_message;
+ message.append(getString(baseMessageResId, getVpnLabel()));
+ message.append(getString(R.string.always_on_disconnected_message_separator));
+ message.append(getString(R.string.always_on_disconnected_message_settings_link),
+ new VpnSpan(), 0 /*flags*/);
+ return message;
+ }
+
+ private class VpnSpan extends ClickableSpan {
+ @Override
+ public void onClick(View unused) {
+ final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ }
+ }
+}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 2fe6648..01dca7e 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -54,12 +54,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (getCallingPackage() != null) {
- Log.e(TAG, getCallingPackage() + " cannot start this activity");
- finish();
- return;
- }
-
try {
mService = IConnectivityManager.Stub.asInterface(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a59844d..1859d35 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2394,6 +2394,7 @@
public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14;
+ public static final int MSG_INIT_SERVICE = 15;
public MainHandler(Looper looper) {
super(looper);
@@ -2492,6 +2493,11 @@
case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: {
showAccessibilityButtonTargetSelection();
} break;
+
+ case MSG_INIT_SERVICE: {
+ final Service service = (Service) msg.obj;
+ service.initializeService();
+ } break;
}
}
@@ -2947,20 +2953,31 @@
if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {
userState.mBindingServices.remove(mComponentName);
mWasConnectedAndDied = false;
- try {
- mServiceInterface.init(this, mId, mOverlayWindowToken);
- onUserStateChangedLocked(userState);
- } catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error while setting connection for service: "
- + service, re);
- binderDied();
- }
+ onUserStateChangedLocked(userState);
+ // Initialize the service on the main handler after we're done setting up for
+ // the new configuration (for example, initializing the input filter).
+ mMainHandler.obtainMessage(MainHandler.MSG_INIT_SERVICE, this).sendToTarget();
} else {
binderDied();
}
}
}
+ private void initializeService() {
+ final IAccessibilityServiceClient serviceInterface;
+ synchronized (mLock) {
+ serviceInterface = mServiceInterface;
+ }
+ if (serviceInterface == null) return;
+ try {
+ serviceInterface.init(this, mId, mOverlayWindowToken);
+ } catch (RemoteException re) {
+ Slog.w(LOG_TAG, "Error while setting connection for service: "
+ + serviceInterface, re);
+ binderDied();
+ }
+ }
+
private boolean isCalledForCurrentUserLocked() {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -3310,8 +3327,8 @@
}
if (mMotionEventInjector != null) {
List<GestureDescription.GestureStep> steps = gestureSteps.getList();
- mMotionEventInjector.injectEvents(steps, mServiceInterface, sequence);
- return;
+ mMotionEventInjector.injectEvents(steps, mServiceInterface, sequence);
+ return;
} else {
Slog.e(LOG_TAG, "MotionEventInjector installation timed out");
}
@@ -3450,18 +3467,15 @@
return region;
}
MagnificationController magnificationController = getMagnificationController();
- boolean forceRegistration = mSecurityPolicy.canControlMagnification(this);
- boolean initiallyRegistered = magnificationController.isRegisteredLocked();
- if (!initiallyRegistered && forceRegistration) {
- magnificationController.register();
- }
+ boolean registeredJustForThisCall =
+ registerMagnificationIfNeeded(magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
magnificationController.getMagnificationRegion(region);
return region;
} finally {
Binder.restoreCallingIdentity(identity);
- if (!initiallyRegistered && forceRegistration) {
+ if (registeredJustForThisCall) {
magnificationController.unregister();
}
}
@@ -3475,11 +3489,17 @@
return 0.0f;
}
}
+ MagnificationController magnificationController = getMagnificationController();
+ boolean registeredJustForThisCall =
+ registerMagnificationIfNeeded(magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- return getMagnificationController().getCenterX();
+ return magnificationController.getCenterX();
} finally {
Binder.restoreCallingIdentity(identity);
+ if (registeredJustForThisCall) {
+ magnificationController.unregister();
+ }
}
}
@@ -3490,14 +3510,30 @@
return 0.0f;
}
}
+ MagnificationController magnificationController = getMagnificationController();
+ boolean registeredJustForThisCall =
+ registerMagnificationIfNeeded(magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- return getMagnificationController().getCenterY();
+ return magnificationController.getCenterY();
} finally {
Binder.restoreCallingIdentity(identity);
+ if (registeredJustForThisCall) {
+ magnificationController.unregister();
+ }
}
}
+ private boolean registerMagnificationIfNeeded(
+ MagnificationController magnificationController) {
+ if (!magnificationController.isRegisteredLocked()
+ && mSecurityPolicy.canControlMagnification(this)) {
+ magnificationController.register();
+ return true;
+ }
+ return false;
+ }
+
@Override
public boolean resetMagnification(boolean animate) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 71f699c..e854079 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -655,6 +655,19 @@
}
@Override
+ public void dismissUi() {
+ final UserHandle user = getCallingUserHandle();
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(
+ user.getIdentifier());
+ if (service != null) {
+ service.dismissUi();
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 751c054..841b1a5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -41,7 +41,6 @@
import android.os.Looper;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutofillService;
@@ -635,6 +634,12 @@
}
}
+ void dismissUi() {
+ if (sVerbose) Slog.v(TAG, "dismissUi()");
+
+ mUi.hideAll(null);
+ }
+
private void sendStateToClients(boolean resetClient) {
final RemoteCallbackList<IAutoFillManagerClient> clients;
final int userClientCount;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 13aca87..796b425 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1567,6 +1567,17 @@
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+
+ synchronized (mTetheringStatsProviders) {
+ for (ITetheringStatsProvider provider : mTetheringStatsProviders.keySet()) {
+ try {
+ provider.setInterfaceQuota(iface, quotaBytes);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Problem setting tethering data limit on provider " +
+ mTetheringStatsProviders.get(provider) + ": " + e);
+ }
+ }
+ }
}
}
@@ -1593,6 +1604,17 @@
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+
+ synchronized (mTetheringStatsProviders) {
+ for (ITetheringStatsProvider provider : mTetheringStatsProviders.keySet()) {
+ try {
+ provider.setInterfaceQuota(iface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Problem removing tethering data limit on provider " +
+ mTetheringStatsProviders.get(provider) + ": " + e);
+ }
+ }
+ }
}
}
@@ -1864,6 +1886,11 @@
}
return stats;
}
+
+ @Override
+ public void setInterfaceQuota(String iface, long quotaBytes) {
+ // Do nothing. netd is already informed of quota changes in setInterfaceQuota.
+ }
}
@Override
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index c6f2fc0..7e90c92 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5330,25 +5330,25 @@
long identityToken = Binder.clearCallingIdentity();
try {
packages = mPackageManager.getPackagesForUid(callingUid);
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- if (packages == null) {
- Log.d(TAG, "No packages for callingUid " + callingUid);
- return false;
- }
- for (String name : packages) {
- try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
- if (packageInfo != null
- && (packageInfo.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ if (packages == null) {
+ Log.d(TAG, "No packages for callingUid " + callingUid);
return false;
}
+ for (String name : packages) {
+ try {
+ PackageInfo packageInfo =
+ mPackageManager.getPackageInfo(name, 0 /* flags */);
+ if (packageInfo != null
+ && (packageInfo.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "Package not found " + e.getMessage());
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
}
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 90df649..81d6fd0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1346,7 +1346,7 @@
* Set while we are running a voice interaction. This overrides
* sleeping while it is active.
*/
- private IVoiceInteractionSession mRunningVoice;
+ IVoiceInteractionSession mRunningVoice;
/**
* For some direct access we need to power manager.
@@ -1366,13 +1366,6 @@
private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
/**
- * A list of tokens that cause the top activity to be put to sleep.
- * They are used by components that may hide and block interaction with underlying
- * activities.
- */
- final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
-
- /**
* Set if we are shutting down the system, similar to sleeping.
*/
boolean mShuttingDown = false;
@@ -12395,7 +12388,19 @@
void onWakefulnessChanged(int wakefulness) {
synchronized(this) {
mWakefulness = wakefulness;
- updateSleepIfNeededLocked();
+
+ // Also update state in a special way for running foreground services UI.
+ switch (mWakefulness) {
+ case PowerManagerInternal.WAKEFULNESS_ASLEEP:
+ case PowerManagerInternal.WAKEFULNESS_DREAMING:
+ case PowerManagerInternal.WAKEFULNESS_DOZING:
+ mServices.updateScreenStateLocked(false /* screenOn */);
+ break;
+ case PowerManagerInternal.WAKEFULNESS_AWAKE:
+ default:
+ mServices.updateScreenStateLocked(true /* screenOn */);
+ break;
+ }
}
}
@@ -12415,14 +12420,24 @@
}
void updateSleepIfNeededLocked() {
- final boolean shouldSleep = shouldSleepLocked();
- if (mSleeping && !shouldSleep) {
- mSleeping = false;
- startTimeTrackingFocusedActivityLocked();
- mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
- mStackSupervisor.comeOutOfSleepIfNeededLocked();
- sendNotifyVrManagerOfSleepState(false);
- updateOomAdjLocked();
+ final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+ final boolean wasSleeping = mSleeping;
+
+ if (!shouldSleep) {
+ // If wasSleeping is true, we need to wake up activity manager state from when
+ // we started sleeping. In either case, we need to apply the sleep tokens, which
+ // will wake up stacks or put them to sleep as appropriate.
+ if (wasSleeping) {
+ mSleeping = false;
+ startTimeTrackingFocusedActivityLocked();
+ mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+ mStackSupervisor.comeOutOfSleepIfNeededLocked();
+ }
+ mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+ if (wasSleeping) {
+ sendNotifyVrManagerOfSleepState(false);
+ updateOomAdjLocked();
+ }
} else if (!mSleeping && shouldSleep) {
mSleeping = true;
if (mCurAppTimeTracker != null) {
@@ -12433,40 +12448,6 @@
sendNotifyVrManagerOfSleepState(true);
updateOomAdjLocked();
}
-
- // Also update state in a special way for running foreground services UI.
- switch (mWakefulness) {
- case PowerManagerInternal.WAKEFULNESS_ASLEEP:
- case PowerManagerInternal.WAKEFULNESS_DREAMING:
- case PowerManagerInternal.WAKEFULNESS_DOZING:
- mServices.updateScreenStateLocked(false);
- break;
- case PowerManagerInternal.WAKEFULNESS_AWAKE:
- default:
- mServices.updateScreenStateLocked(true);
- break;
- }
- }
-
- private boolean shouldSleepLocked() {
- // Resume applications while running a voice interactor.
- if (mRunningVoice != null) {
- return false;
- }
-
- // TODO: Transform the lock screen state into a sleep token instead.
- switch (mWakefulness) {
- case PowerManagerInternal.WAKEFULNESS_AWAKE:
- case PowerManagerInternal.WAKEFULNESS_DREAMING:
- // Pause applications whenever the lock screen is shown or any sleep
- // tokens have been acquired.
- return mKeyguardController.isKeyguardShowing() || !mSleepTokens.isEmpty();
- case PowerManagerInternal.WAKEFULNESS_DOZING:
- case PowerManagerInternal.WAKEFULNESS_ASLEEP:
- default:
- // If we're asleep then pause applications unconditionally.
- return true;
- }
}
/** Pokes the task persister. */
@@ -12507,6 +12488,7 @@
synchronized(this) {
mShuttingDown = true;
+ mStackSupervisor.prepareForShutdownLocked();
updateEventDispatchingLocked();
timedout = mStackSupervisor.shutdownLocked(timeout);
}
@@ -14921,6 +14903,14 @@
this, in, out, err, args, callback, resultReceiver);
}
+ SleepToken acquireSleepToken(String tag, int displayId) {
+ synchronized (this) {
+ final SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+ updateSleepIfNeededLocked();
+ return token;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
@@ -15817,7 +15807,7 @@
if (dumpPackage == null) {
pw.println(" mWakefulness="
+ PowerManagerInternal.wakefulnessToString(mWakefulness));
- pw.println(" mSleepTokens=" + mSleepTokens);
+ pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
pw.println(" mSleeping=" + mSleeping);
pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + mTestPssMode);
if (mRunningVoice != null) {
@@ -23778,15 +23768,9 @@
}
@Override
- public SleepToken acquireSleepToken(String tag) {
+ public SleepToken acquireSleepToken(String tag, int displayId) {
Preconditions.checkNotNull(tag);
-
- synchronized (ActivityManagerService.this) {
- SleepTokenImpl token = new SleepTokenImpl(tag);
- mSleepTokens.add(token);
- updateSleepIfNeededLocked();
- return token;
- }
+ return ActivityManagerService.this.acquireSleepToken(tag, displayId);
}
@Override
@@ -24223,30 +24207,6 @@
}
}
- private final class SleepTokenImpl extends SleepToken {
- private final String mTag;
- private final long mAcquireTime;
-
- public SleepTokenImpl(String tag) {
- mTag = tag;
- mAcquireTime = SystemClock.uptimeMillis();
- }
-
- @Override
- public void release() {
- synchronized (ActivityManagerService.this) {
- if (mSleepTokens.remove(this)) {
- updateSleepIfNeededLocked();
- }
- }
- }
-
- @Override
- public String toString() {
- return "{\"" + mTag + "\", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
- }
- }
-
/**
* An implementation of IAppTask, that allows an app to manage its own tasks via
* {@link android.app.ActivityManager.AppTask}. We keep track of the callingUid to ensure that
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index df70a93..e0bcbe7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -749,9 +749,11 @@
static class Token extends IApplicationToken.Stub {
private final WeakReference<ActivityRecord> weakActivity;
+ private final String name;
- Token(ActivityRecord activity) {
+ Token(ActivityRecord activity, Intent intent) {
weakActivity = new WeakReference<>(activity);
+ name = intent.getComponent().flattenToShortString();
}
private static ActivityRecord tokenToActivityRecordLocked(Token token) {
@@ -775,6 +777,11 @@
sb.append('}');
return sb.toString();
}
+
+ @Override
+ public String getName() {
+ return name;
+ }
}
static ActivityRecord forTokenLocked(IBinder token) {
@@ -798,7 +805,7 @@
ActivityStackSupervisor supervisor, ActivityOptions options,
ActivityRecord sourceRecord) {
service = _service;
- appToken = new Token(this);
+ appToken = new Token(this, _intent);
info = aInfo;
launchedFromPid = _launchedFromPid;
launchedFromUid = _launchedFromUid;
@@ -1341,7 +1348,9 @@
intent, getUriPermissionsLocked(), userId);
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
- final boolean isTopActivityWhileSleeping = service.isSleepingLocked() && isTopRunningActivity();
+ final ActivityStack stack = getStack();
+ final boolean isTopActivityWhileSleeping = isTopRunningActivity()
+ && (stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked());
// We want to immediately deliver the intent to the activity if:
// - It is currently resumed or paused. i.e. it is currently visible to the user and we want
@@ -1731,7 +1740,7 @@
// If the screen is going to turn on because the caller explicitly requested it and
// the keyguard is not showing don't attempt to sleep. Otherwise the Activity will
// pause and then resume again later, which will result in a double life-cycle event.
- mStackSupervisor.checkReadyForSleepLocked();
+ stack.checkReadyForSleep();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5f8e418..e9f3608 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1171,10 +1171,25 @@
}
}
+ void checkReadyForSleep() {
+ if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
+ mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
+ }
+ }
+
/**
+ * Tries to put the activities in the stack to sleep.
+ *
+ * If the stack is not in a state where its activities can be put to sleep, this function will
+ * start any necessary actions to move the stack into such a state. It is expected that this
+ * function get called again when those actions complete.
+ *
+ * @param shuttingDown true when the called because the device is shutting down.
* @return true if something must be done before going to sleep.
*/
- boolean checkReadyForSleepLocked() {
+ boolean goToSleepIfPossible(boolean shuttingDown) {
+ boolean shouldSleep = true;
+
if (mResumedActivity != null) {
// Still have something resumed; can't sleep until it is paused.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
@@ -1184,26 +1199,47 @@
// If we are in the middle of resuming the top activity in
// {@link #resumeTopActivityUncheckedLocked}, mResumedActivity will be set but not
// resumed yet. We must not proceed pausing the activity here. This method will be
- // called again if necessary as part of
+ // called again if necessary as part of {@link #checkReadyForSleep} or
// {@link ActivityStackSupervisor#checkReadyForSleepLocked}.
if (mStackSupervisor.inResumeTopActivity) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "In the middle of resuming top activity "
+ mResumedActivity);
- return true;
+ } else {
+ startPausingLocked(false, true, null, false);
}
-
- startPausingLocked(false, true, null, false);
- return true;
- }
- if (mPausingActivity != null) {
+ shouldSleep = false ;
+ } else if (mPausingActivity != null) {
// Still waiting for something to pause; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
- return true;
+ shouldSleep = false;
}
- return false;
+
+ if (!shuttingDown) {
+ if (containsActivityFromStack(mStackSupervisor.mStoppingActivities)) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
+ + mStackSupervisor.mStoppingActivities.size() + " activities");
+
+ mStackSupervisor.scheduleIdleLocked();
+ shouldSleep = false;
+ }
+
+ if (containsActivityFromStack(mStackSupervisor.mGoingToSleepActivities)) {
+ // Still need to tell some activities to sleep; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
+ + mStackSupervisor.mGoingToSleepActivities.size() + " activities");
+ shouldSleep = false;
+ }
+ }
+
+ if (shouldSleep) {
+ goToSleep();
+ }
+
+ return !shouldSleep;
}
- void goToSleep() {
+ private void goToSleep() {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Make sure any paused or stopped but visible activities are now sleeping.
@@ -1220,6 +1256,15 @@
}
}
+ private boolean containsActivityFromStack(List<ActivityRecord> rs) {
+ for (ActivityRecord r : rs) {
+ if (r.getStack() == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
* this directly impacts the responsiveness seen by the user.
@@ -1252,7 +1297,7 @@
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.state);
- if (!mService.isSleepingLocked()) {
+ if (!shouldSleepActivities()) {
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
@@ -1412,7 +1457,7 @@
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
prev.state = STOPPING;
- } else if (!prev.visible || mService.isSleepingOrShuttingDownLocked()) {
+ } else if (!prev.visible || shouldSleepOrShutDownActivities()) {
// Clear out any deferred client hide we might currently have.
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
@@ -1434,10 +1479,10 @@
if (resumeNext) {
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
- if (!mService.isSleepingOrShuttingDownLocked()) {
+ if (!topStack.shouldSleepOrShutDownActivities()) {
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
- mStackSupervisor.checkReadyForSleepLocked();
+ checkReadyForSleep();
ActivityRecord top = topStack.topRunningActivityLocked();
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run, do resume anyway to start
@@ -1503,7 +1548,7 @@
mStackSupervisor.scheduleIdleTimeoutLocked(r);
}
} else {
- mStackSupervisor.checkReadyForSleepLocked();
+ checkReadyForSleep();
}
}
@@ -2212,7 +2257,7 @@
// is skipped.
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
if (next == null || !next.canTurnScreenOn()) {
- mStackSupervisor.checkReadyForSleepLocked();
+ checkReadyForSleep();
}
return result;
@@ -2297,7 +2342,7 @@
// If we are sleeping, and there is no resumed activity, and the top
// activity is paused, well that is the state we want.
- if (mService.isSleepingOrShuttingDownLocked()
+ if (shouldSleepOrShutDownActivities()
&& mLastPausedActivity == next
&& mStackSupervisor.allPausedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
@@ -2389,7 +2434,7 @@
// If the most recent activity was noHistory but was only stopped rather
// than stopped+finished because the device went to sleep, we need to make
// sure to finish it as we're making a new activity topmost.
- if (mService.isSleepingLocked() && mLastNoHistoryActivity != null &&
+ if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
!mLastNoHistoryActivity.finishing) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"no-history finish of " + mLastNoHistoryActivity + " on new resume");
@@ -3401,7 +3446,7 @@
if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
if (!r.finishing) {
- if (!mService.isSleepingLocked()) {
+ if (!shouldSleepActivities()) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"stop-no-history", false)) {
@@ -3433,7 +3478,7 @@
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
- if (mService.isSleepingOrShuttingDownLocked()) {
+ if (shouldSleepOrShutDownActivities()) {
r.setSleeping(true);
}
Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
@@ -5279,4 +5324,13 @@
mNoAnimActivities.clear();
ActivityOptions.abort(options);
}
+
+ boolean shouldSleepActivities() {
+ final ActivityStackSupervisor.ActivityDisplay display = getDisplay();
+ return display != null ? display.isSleeping() : mService.isSleepingLocked();
+ }
+
+ boolean shouldSleepOrShutDownActivities() {
+ return shouldSleepActivities() || mService.isShuttingDownLocked();
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 23a4cac..1568e8c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -105,6 +105,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.ProfilerInfo;
@@ -155,6 +156,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
@@ -177,6 +179,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -375,9 +378,6 @@
* is being brought in front of us. */
boolean mUserLeaving = false;
- /** Set when we have taken too long waiting to go to sleep. */
- boolean mSleepTimeout = false;
-
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -393,6 +393,13 @@
*/
PowerManager.WakeLock mGoingToSleep;
+ /**
+ * A list of tokens that cause the top activity to be put to sleep.
+ * They are used by components that may hide and block interaction with underlying
+ * activities.
+ */
+ final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>();
+
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
@@ -3126,6 +3133,16 @@
return null;
}
+ boolean hasAwakeDisplay() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ if (!display.shouldSleep()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void goingToSleepLocked() {
scheduleSleepTimeout();
if (!mGoingToSleep.isHeld()) {
@@ -3138,7 +3155,16 @@
mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
}
}
- checkReadyForSleepLocked();
+
+ applySleepTokensLocked(false /* applyToStacks */);
+
+ checkReadyForSleepLocked(true /* allowDelay */);
+ }
+
+ void prepareForShutdownLocked() {
+ for (int i = 0; i < mActivityDisplays.size(); i++) {
+ createSleepTokenLocked("shutdown", mActivityDisplays.keyAt(i));
+ }
}
boolean shutdownLocked(int timeout) {
@@ -3151,7 +3177,8 @@
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- cantShutdown |= stacks.get(stackNdx).checkReadyForSleepLocked();
+ cantShutdown |=
+ stacks.get(stackNdx).goToSleepIfPossible(true /* shuttingDown */);
}
}
if (cantShutdown) {
@@ -3172,8 +3199,7 @@
}
// Force checkReadyForSleep to complete.
- mSleepTimeout = true;
- checkReadyForSleepLocked();
+ checkReadyForSleepLocked(false /* allowDelay */);
return timedout;
}
@@ -3183,54 +3209,75 @@
if (mGoingToSleep.isHeld()) {
mGoingToSleep.release();
}
+ }
+
+ void applySleepTokensLocked(boolean applyToStacks) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ // Set the sleeping state of the display.
+ final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final boolean displayShouldSleep = display.shouldSleep();
+ if (displayShouldSleep == display.isSleeping()) {
+ continue;
+ }
+ display.setIsSleeping(displayShouldSleep);
+
+ if (!applyToStacks) {
+ continue;
+ }
+
+ // Set the sleeping state of the stacks on the display.
+ final ArrayList<ActivityStack> stacks = display.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- stack.awakeFromSleepingLocked();
- if (isFocusedStack(stack)) {
- resumeFocusedStackTopActivityLocked();
+ if (displayShouldSleep) {
+ stack.goToSleepIfPossible(false /* shuttingDown */);
+ } else {
+ stack.awakeFromSleepingLocked();
+ if (isFocusedStack(stack)) {
+ resumeFocusedStackTopActivityLocked();
+ }
+ }
+ }
+
+ if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
+ continue;
+ }
+ // The display is awake now, so clean up the going to sleep list.
+ for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+ final ActivityRecord r = it.next();
+ if (r.getDisplayId() == display.mDisplayId) {
+ it.remove();
}
}
}
- mGoingToSleepActivities.clear();
}
void activitySleptLocked(ActivityRecord r) {
mGoingToSleepActivities.remove(r);
- checkReadyForSleepLocked();
+ final ActivityStack s = r.getStack();
+ if (s != null) {
+ s.checkReadyForSleep();
+ } else {
+ checkReadyForSleepLocked(true);
+ }
}
- void checkReadyForSleepLocked() {
+ void checkReadyForSleepLocked(boolean allowDelay) {
if (!mService.isSleepingOrShuttingDownLocked()) {
// Do not care.
return;
}
- if (!mSleepTimeout) {
+ if (allowDelay) {
boolean dontSleep = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ArrayList<ActivityStack> stacks = display.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- dontSleep |= stacks.get(stackNdx).checkReadyForSleepLocked();
+ dontSleep |= stacks.get(stackNdx).goToSleepIfPossible(false /* shuttingDown */);
}
}
- if (mStoppingActivities.size() > 0) {
- // Still need to tell some activities to stop; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
- + mStoppingActivities.size() + " activities");
- scheduleIdleLocked();
- dontSleep = true;
- }
-
- if (mGoingToSleepActivities.size() > 0) {
- // Still need to tell some activities to sleep; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
- + mGoingToSleepActivities.size() + " activities");
- dontSleep = true;
- }
-
if (dontSleep) {
return;
}
@@ -3239,13 +3286,6 @@
// Send launch end powerhint before going sleep
mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
- for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- stacks.get(stackNdx).goToSleep();
- }
- }
-
removeSleepTimeouts();
if (mGoingToSleep.isHeld()) {
@@ -3516,21 +3556,27 @@
s.setVisibility(false);
}
}
- if ((!waitingVisible || mService.isSleepingOrShuttingDownLocked()) && remove) {
- if (!processPausingActivities && s.state == PAUSING) {
- // Defer processing pausing activities in this iteration and reschedule
- // a delayed idle to reprocess it again
- removeTimeoutsForActivityLocked(idleActivity);
- scheduleIdleTimeoutLocked(idleActivity);
- continue;
- }
+ if (remove) {
+ final ActivityStack stack = s.getStack();
+ final boolean shouldSleepOrShutDown = stack != null
+ ? stack.shouldSleepOrShutDownActivities()
+ : mService.isSleepingOrShuttingDownLocked();
+ if (!waitingVisible || shouldSleepOrShutDown) {
+ if (!processPausingActivities && s.state == PAUSING) {
+ // Defer processing pausing activities in this iteration and reschedule
+ // a delayed idle to reprocess it again
+ removeTimeoutsForActivityLocked(idleActivity);
+ scheduleIdleTimeoutLocked(idleActivity);
+ continue;
+ }
- if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
- if (stops == null) {
- stops = new ArrayList<>();
+ if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
+ if (stops == null) {
+ stops = new ArrayList<>();
+ }
+ stops.add(s);
+ mStoppingActivities.remove(activityNdx);
}
- stops.add(s);
- mStoppingActivities.remove(activityNdx);
}
}
@@ -3583,7 +3629,6 @@
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
- pw.print(prefix); pw.println("mSleepTimeout=" + mSleepTimeout);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
@@ -3680,6 +3725,8 @@
stackHeader.append("\n");
stackHeader.append(" mFullscreen=" + stack.mFullscreen);
stackHeader.append("\n");
+ stackHeader.append(" isSleeping=" + stack.shouldSleepActivities());
+ stackHeader.append("\n");
stackHeader.append(" mBounds=" + stack.mBounds);
final boolean printedStackHeader = stack.dumpActivitiesLocked(fd, pw, dumpAll,
@@ -3731,8 +3778,6 @@
" Activities waiting for another to become visible:", null);
printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
false, dumpPackage, true, " Activities waiting to sleep:", null);
- printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to sleep:", null);
return printed;
}
@@ -3843,7 +3888,6 @@
}
void removeSleepTimeouts() {
- mSleepTimeout = false;
mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
}
@@ -3945,6 +3989,9 @@
moveTasksToFullscreenStackLocked(stack.getStackId(), true /* onTop */);
}
}
+
+ releaseSleepTokens(activityDisplay);
+
mActivityDisplays.remove(displayId);
mWindowManager.onDisplayRemoved(displayId);
}
@@ -3955,12 +4002,60 @@
synchronized (mService) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay != null) {
+ // The window policy is responsible for stopping activities on the default display
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ int displayState = activityDisplay.mDisplay.getState();
+ if (displayState == Display.STATE_OFF && activityDisplay.mOffToken == null) {
+ activityDisplay.mOffToken =
+ mService.acquireSleepToken("Display-off", displayId);
+ } else if (displayState == Display.STATE_ON
+ && activityDisplay.mOffToken != null) {
+ activityDisplay.mOffToken.release();
+ activityDisplay.mOffToken = null;
+ }
+ }
// TODO: Update the bounds.
}
mWindowManager.onDisplayChanged(displayId);
}
}
+ SleepToken createSleepTokenLocked(String tag, int displayId) {
+ ActivityDisplay display = mActivityDisplays.get(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException("Invalid display: " + displayId);
+ }
+
+ final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+ mSleepTokens.add(token);
+ display.mAllSleepTokens.add(token);
+ return token;
+ }
+
+ private void removeSleepTokenLocked(SleepTokenImpl token) {
+ mSleepTokens.remove(token);
+
+ ActivityDisplay display = mActivityDisplays.get(token.mDisplayId);
+ if (display != null) {
+ display.mAllSleepTokens.remove(token);
+ if (display.mAllSleepTokens.isEmpty()) {
+ mService.updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ private void releaseSleepTokens(ActivityDisplay display) {
+ if (display.mAllSleepTokens.isEmpty()) {
+ return;
+ }
+ for (SleepTokenImpl token : display.mAllSleepTokens) {
+ mSleepTokens.remove(token);
+ }
+ display.mAllSleepTokens.clear();
+
+ mService.updateSleepIfNeededLocked();
+ }
+
private StackInfo getStackInfoLocked(ActivityStack stack) {
final int displayId = stack.mDisplayId;
final ActivityDisplay display = mActivityDisplays.get(displayId);
@@ -4266,9 +4361,9 @@
void activityRelaunchedLocked(IBinder token) {
mWindowManager.notifyAppRelaunchingFinished(token);
- if (mService.isSleepingOrShuttingDownLocked()) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r != null) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ if (r.getStack().shouldSleepOrShutDownActivities()) {
r.setSleeping(true, true);
}
}
@@ -4420,8 +4515,7 @@
synchronized (mService) {
if (mService.isSleepingOrShuttingDownLocked()) {
Slog.w(TAG, "Sleep timeout! Sleeping now.");
- mSleepTimeout = true;
- checkReadyForSleepLocked();
+ checkReadyForSleepLocked(false /* allowDelay */);
}
}
} break;
@@ -4546,6 +4640,13 @@
/** Array of all UIDs that are present on the display. */
private IntArray mDisplayAccessUIDs = new IntArray();
+ /** All tokens used to put activities on this stack to sleep (including mOffToken) */
+ final ArrayList<SleepTokenImpl> mAllSleepTokens = new ArrayList<>();
+ /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */
+ SleepToken mOffToken;
+
+ private boolean mSleeping;
+
@VisibleForTesting
ActivityDisplay() {
mActivityDisplays.put(mDisplayId, this);
@@ -4570,12 +4671,14 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
+ " to displayId=" + mDisplayId + " position=" + position);
mStacks.add(position, stack);
+ mService.updateSleepIfNeededLocked();
}
void detachStack(ActivityStack stack) {
if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
+ mService.updateSleepIfNeededLocked();
}
@Override
@@ -4623,6 +4726,19 @@
boolean shouldDestroyContentOnRemove() {
return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
}
+
+ boolean shouldSleep() {
+ return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
+ && (mService.mRunningVoice == null);
+ }
+
+ boolean isSleeping() {
+ return mSleeping;
+ }
+
+ void setIsSleeping(boolean asleep) {
+ mSleeping = asleep;
+ }
}
ActivityStack findStackBehind(ActivityStack stack) {
@@ -4804,4 +4920,30 @@
mResult.dump(pw, prefix);
}
}
+
+ private final class SleepTokenImpl extends SleepToken {
+ private final String mTag;
+ private final long mAcquireTime;
+ private final int mDisplayId;
+
+ public SleepTokenImpl(String tag, int displayId) {
+ mTag = tag;
+ mDisplayId = displayId;
+ mAcquireTime = SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public void release() {
+ synchronized (mService) {
+ removeSleepTokenLocked(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{\"" + mTag + "\", display " + mDisplayId
+ + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 58e71df..cea80c8 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -32,6 +33,7 @@
import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import android.app.ActivityManagerInternal.SleepToken;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -63,6 +65,7 @@
private ActivityRecord mDismissingKeyguardActivity;
private int mBeforeUnoccludeTransit;
private int mVisibilityTransactionDepth;
+ private SleepToken mSleepToken;
KeyguardController(ActivityManagerService service,
ActivityStackSupervisor stackSupervisor) {
@@ -102,7 +105,7 @@
mDismissalRequested = false;
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mService.updateSleepIfNeededLocked();
+ updateKeyguardSleepToken();
}
/**
@@ -122,7 +125,7 @@
mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
false /* alwaysKeepCurrent */, convertTransitFlags(flags),
false /* forceOverride */);
- mService.updateSleepIfNeededLocked();
+ updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -263,7 +266,7 @@
try {
mWindowManager.prepareAppTransition(resolveOccludeTransit(),
false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
- mService.updateSleepIfNeededLocked();
+ updateKeyguardSleepToken();
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
} finally {
@@ -333,6 +336,15 @@
}
}
+ private void updateKeyguardSleepToken() {
+ if (mSleepToken == null && isKeyguardShowing()) {
+ mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
+ } else if (mSleepToken != null && !isKeyguardShowing()) {
+ mSleepToken.release();
+ mSleepToken = null;
+ }
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "KeyguardController:");
pw.println(prefix + " mKeyguardShowing=" + mKeyguardShowing);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e82eabf..225d708 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1416,7 +1416,11 @@
notificationManager.cancelAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, user);
return;
}
- final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
+ final Intent intent = new Intent();
+ intent.setComponent(ComponentName.unflattenFromString(mContext.getString(
+ R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)));
+ intent.putExtra("lockdown", mLockdown);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final PendingIntent configIntent = mSystemServices.pendingIntentGetActivityAsUser(
intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, user);
final Notification.Builder builder =
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 1a5ff77..55e290a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -46,7 +46,6 @@
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -58,8 +57,6 @@
public class OffloadController {
private static final String TAG = OffloadController.class.getSimpleName();
- private static final int STATS_FETCH_TIMEOUT_MS = 1000;
-
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
@@ -76,9 +73,17 @@
private Set<String> mLastLocalPrefixStrs;
// Maps upstream interface names to offloaded traffic statistics.
+ // Always contains the latest value received from the hardware for each interface, regardless of
+ // whether offload is currently running on that interface.
private HashMap<String, OffloadHardwareInterface.ForwardedStats>
mForwardedStats = new HashMap<>();
+ // Maps upstream interface names to interface quotas.
+ // Always contains the latest value received from the framework for each interface, regardless
+ // of whether offload is currently running (or is even supported) on that interface. Only
+ // includes upstream interfaces that have a quota set.
+ private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+
public OffloadController(Handler h, OffloadHardwareInterface hwi,
ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
mHandler = h;
@@ -177,36 +182,38 @@
@Override
public NetworkStats getTetherStats() {
NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- CountDownLatch latch = new CountDownLatch(1);
- mHandler.post(() -> {
- try {
- NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.set = SET_DEFAULT;
- entry.tag = TAG_NONE;
- entry.uid = UID_TETHERING;
+ // We can't just post to mHandler because we are mostly (but not always) called by
+ // NetworkStatsService#performPollLocked, which is (currently) on the same thread as us.
+ mHandler.runWithScissors(() -> {
+ NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.set = SET_DEFAULT;
+ entry.tag = TAG_NONE;
+ entry.uid = UID_TETHERING;
- updateStatsForCurrentUpstream();
+ updateStatsForCurrentUpstream();
- for (String iface : mForwardedStats.keySet()) {
- entry.iface = iface;
- entry.rxBytes = mForwardedStats.get(iface).rxBytes;
- entry.txBytes = mForwardedStats.get(iface).txBytes;
- stats.addValues(entry);
- }
- } finally {
- latch.countDown();
+ for (String iface : mForwardedStats.keySet()) {
+ entry.iface = iface;
+ entry.rxBytes = mForwardedStats.get(iface).rxBytes;
+ entry.txBytes = mForwardedStats.get(iface).txBytes;
+ stats.addValues(entry);
}
- });
-
- try {
- latch.await(STATS_FETCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- mLog.e("Tethering stats fetch timed out after " + STATS_FETCH_TIMEOUT_MS + "ms");
- }
+ }, 0);
return stats;
}
+
+ public void setInterfaceQuota(String iface, long quotaBytes) {
+ mHandler.post(() -> {
+ if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
+ mInterfaceQuotas.remove(iface);
+ } else {
+ mInterfaceQuotas.put(iface, quotaBytes);
+ }
+ maybeUpdateDataLimit(iface);
+ });
+ }
}
private void maybeUpdateStats(String iface) {
@@ -220,6 +227,22 @@
mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
}
+ private boolean maybeUpdateDataLimit(String iface) {
+ // setDataLimit may only be called while offload is occuring on this upstream.
+ if (!started() ||
+ mUpstreamLinkProperties == null ||
+ !TextUtils.equals(iface, mUpstreamLinkProperties.getInterfaceName())) {
+ return true;
+ }
+
+ Long limit = mInterfaceQuotas.get(iface);
+ if (limit == null) {
+ limit = Long.MAX_VALUE;
+ }
+
+ return mHwInterface.setDataLimit(iface, limit);
+ }
+
private void updateStatsForCurrentUpstream() {
if (mUpstreamLinkProperties != null) {
maybeUpdateStats(mUpstreamLinkProperties.getInterfaceName());
@@ -309,8 +332,21 @@
}
}
- return mHwInterface.setUpstreamParameters(
+ boolean success = mHwInterface.setUpstreamParameters(
iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
+
+ if (!success) {
+ return success;
+ }
+
+ // Data limits can only be set once offload is running on the upstream.
+ success = maybeUpdateDataLimit(iface);
+ if (!success) {
+ mLog.log("Setting data limit for " + iface + " failed, disabling offload.");
+ stop();
+ }
+
+ return success;
}
private boolean computeAndPushLocalPrefixes() {
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 25fab9e..86ff0a6 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -40,7 +40,7 @@
// Change this value to control whether tether offload is enabled or
// disabled by default in the absence of an explicit Settings value.
// See accompanying unittest to distinguish 0 from non-0 values.
- private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 1;
+ private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
private static final String NO_INTERFACE_NAME = "";
private static final String NO_IPV4_ADDRESS = "";
private static final String NO_IPV4_GATEWAY = "";
@@ -188,6 +188,27 @@
return results.success;
}
+ public boolean setDataLimit(String iface, long limit) {
+
+ final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
+
+ final CbResults results = new CbResults();
+ try {
+ mOffloadControl.setDataLimit(
+ iface, limit,
+ (boolean success, String errMsg) -> {
+ results.success = success;
+ results.errMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.success;
+ }
+
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
iface = (iface != null) ? iface : NO_INTERFACE_NAME;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 8087813..beaddb4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -212,12 +212,25 @@
}
}
- protected void onSettingRestored(String element, String value, int userId) {
+ protected void onSettingRestored(String element, String value, int backupSdkInt, int userId) {
if (!mUseXml) {
Slog.d(TAG, "Restored managed service setting: " + element);
if (mConfig.secureSettingName.equals(element) ||
(mConfig.secondarySettingName != null
&& mConfig.secondarySettingName.equals(element))) {
+ if (backupSdkInt < Build.VERSION_CODES.O) {
+ // automatic system grants were added in O, so append the approved apps
+ // rather than wiping out the setting
+ String currentSetting =
+ getApproved(userId, mConfig.secureSettingName.equals(element));
+ if (!TextUtils.isEmpty(currentSetting)) {
+ if (!TextUtils.isEmpty(value)) {
+ value = value + ENABLED_SERVICES_SEPARATOR + currentSetting;
+ } else {
+ value = currentSetting;
+ }
+ }
+ }
Settings.Secure.putStringForUser(
mContext.getContentResolver(), element, value, userId);
loadAllowedComponentsFromSettings();
@@ -370,6 +383,13 @@
}
}
+ protected String getApproved(int userId, boolean primary) {
+ final ArrayMap<Boolean, ArraySet<String>> allowedByType =
+ mApproved.getOrDefault(userId, new ArrayMap<>());
+ ArraySet<String> approved = allowedByType.getOrDefault(primary, new ArraySet<>());
+ return String.join(ENABLED_SERVICES_SEPARATOR, approved);
+ }
+
protected List<ComponentName> getAllowedComponents(int userId) {
final List<ComponentName> allowedComponents = new ArrayList<>();
final ArrayMap<Boolean, ArraySet<String>> allowedByType =
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 186243c..c84c3b6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -816,8 +816,10 @@
String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
int restoredFromSdkInt = intent.getIntExtra(
Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0);
- mListeners.onSettingRestored(element, newValue, getSendingUserId());
- mConditionProviders.onSettingRestored(element, newValue, getSendingUserId());
+ mListeners.onSettingRestored(
+ element, newValue, restoredFromSdkInt, getSendingUserId());
+ mConditionProviders.onSettingRestored(
+ element, newValue, restoredFromSdkInt, getSendingUserId());
} catch (Exception e) {
Slog.wtf(TAG, "Cannot restore managed services from settings", e);
}
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 2100038..30fda1e 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -96,7 +96,7 @@
}
public boolean isInstant() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0;
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
}
public boolean isRuntimeOnly() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 84bb453..56f325f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4246,10 +4246,17 @@
return null;
}
// If the caller is an app that targets pre 26 SDK drop protection flags.
- final PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
+ PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
if (permissionInfo != null) {
- permissionInfo.protectionLevel = adjustPermissionProtectionFlagsLPr(
+ final int protectionLevel = adjustPermissionProtectionFlagsLPr(
permissionInfo.protectionLevel, packageName, callingUid);
+ if (permissionInfo.protectionLevel != protectionLevel) {
+ // If we return different protection level, don't use the cached info
+ if (p.perm != null && p.perm.info == permissionInfo) {
+ permissionInfo = new PermissionInfo(permissionInfo);
+ }
+ permissionInfo.protectionLevel = protectionLevel;
+ }
}
return permissionInfo;
}
@@ -18529,12 +18536,12 @@
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
// Don't allow anyone but the system to define ephemeral permissions.
- if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0
+ if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
&& !systemApp) {
Slog.w(TAG, "Non-System package " + pkg.packageName
+ " attempting to delcare ephemeral permission "
+ perm.info.name + "; Removing ephemeral.");
- perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_EPHEMERAL;
+ perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
}
// Check whether the newly-scanned package wants to define an already-defined perm
if (bp != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ae78d7c..6b23b72 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -115,6 +115,8 @@
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.proto.WindowManagerPolicyProto.STABLE_BOUNDS;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
@@ -194,6 +196,7 @@
import android.util.MutableBoolean;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -7466,10 +7469,12 @@
}
}
+ // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
private void updateDreamingSleepToken(boolean acquire) {
if (acquire) {
if (mDreamingSleepToken == null) {
- mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken("Dream");
+ mDreamingSleepToken = mActivityManagerInternal.acquireSleepToken(
+ "Dream", Display.DEFAULT_DISPLAY);
}
} else {
if (mDreamingSleepToken != null) {
@@ -7479,10 +7484,12 @@
}
}
+ // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
private void updateScreenOffSleepToken(boolean acquire) {
if (acquire) {
if (mScreenOffSleepToken == null) {
- mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken("ScreenOff");
+ mScreenOffSleepToken = mActivityManagerInternal.acquireSleepToken(
+ "ScreenOff", Display.DEFAULT_DISPLAY);
}
} else {
if (mScreenOffSleepToken != null) {
@@ -8218,6 +8225,14 @@
}
@Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ new Rect(mStableLeft, mStableTop, mStableRight, mStableBottom).writeToProto(proto,
+ STABLE_BOUNDS);
+ proto.end(token);
+ }
+
+ @Override
public void dump(String prefix, PrintWriter pw, String[] args) {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 805250a..6484a13 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -429,6 +429,8 @@
}
public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+ // Make sure we're working with the most current bounds
+ mMagnifedViewport.recomputeBoundsLocked();
mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index cda3efd..c19ede0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -47,6 +47,8 @@
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
+import static com.android.server.wm.proto.AppTransitionProto.APP_TRANSITION_STATE;
+import static com.android.server.wm.proto.AppTransitionProto.LAST_USED_APP_TRANSITION;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -65,6 +67,7 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.WindowManager;
@@ -1975,6 +1978,13 @@
}
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(APP_TRANSITION_STATE, mAppTransitionState);
+ proto.write(LAST_USED_APP_TRANSITION, mLastUsedAppTransition);
+ proto.end(token);
+ }
+
@Override
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.println(this);
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ddbbde1..d4791a6 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
@@ -358,7 +358,7 @@
// This must be called while inside a transaction.
boolean stepAnimationLocked(long currentTime) {
- if (mService.okToAnimate()) {
+ if (mAppToken.okToAnimate()) {
// We will run animations as long as the display isn't frozen.
if (animation == sDummyAnimation) {
@@ -423,8 +423,8 @@
if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
+ ": reportedVisible=" + mAppToken.reportedVisible
- + " okToDisplay=" + mService.okToDisplay()
- + " okToAnimate=" + mService.okToAnimate()
+ + " okToDisplay=" + mAppToken.okToDisplay()
+ + " okToAnimate=" + mAppToken.okToAnimate()
+ " startingDisplayed=" + mAppToken.startingDisplayed);
transformation.clear();
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index e9696d2..1ca98ac 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
@@ -401,7 +402,7 @@
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
- if (mService.okToAnimate() && mService.mAppTransition.isTransitionSet()) {
+ if (wtoken.okToAnimate() && mService.mAppTransition.isTransitionSet()) {
// A dummy animation is a placeholder animation which informs others that an
// animation is going on (in this case an application transition). If the animation
// was transferred from another application/animator, no dummy animator should be
@@ -478,7 +479,7 @@
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
- if (!mService.okToDisplay()) {
+ if (!mContainer.okToDisplay()) {
return false;
}
@@ -699,16 +700,17 @@
public void startFreezingScreen(int configChanges) {
synchronized(mWindowMap) {
- if (configChanges == 0 && mService.okToDisplay()) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
- return;
- }
-
if (mContainer == null) {
Slog.w(TAG_WM,
"Attempted to freeze screen with non-existing app token: " + mContainer);
return;
}
+
+ if (configChanges == 0 && mContainer.okToDisplay()) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
+ return;
+ }
+
mContainer.startFreezingScreen();
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2e4de8c..505b4c4 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -49,6 +49,8 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.proto.AppWindowTokenProto.NAME;
+import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN;
import android.annotation.NonNull;
import android.app.Activity;
@@ -57,8 +59,10 @@
import android.os.Binder;
import android.os.Debug;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.IApplicationToken;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -1746,6 +1750,26 @@
}
@Override
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ writeNameToProto(proto, NAME);
+ super.writeToProto(proto, WINDOW_TOKEN);
+ proto.end(token);
+ }
+
+ void writeNameToProto(ProtoOutputStream proto, long fieldId) {
+ if (appToken == null) {
+ return;
+ }
+ try {
+ proto.write(fieldId, appToken.getName());
+ } catch (RemoteException e) {
+ // This shouldn't happen, but in this case fall back to outputting nothing
+ Slog.e(TAG, e.toString());
+ }
+ }
+
+ @Override
public String toString() {
if (stringName == null) {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 7414928..6f9e45a 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -290,7 +290,7 @@
state.dimLayer.setLayer(dimLayer);
}
if (state.dimLayer.isAnimating()) {
- if (!mDisplayContent.mService.okToAnimate()) {
+ if (!mDisplayContent.okToAnimate()) {
// Jump to the end of the animation.
state.dimLayer.show();
} else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 465ca86..2df773e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -97,6 +97,15 @@
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO;
+import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
+import static com.android.server.wm.proto.DisplayProto.DPI;
+import static com.android.server.wm.proto.DisplayProto.ID;
+import static com.android.server.wm.proto.DisplayProto.IME_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER;
+import static com.android.server.wm.proto.DisplayProto.STACKS;
import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
@@ -118,6 +127,7 @@
import android.util.DisplayMetrics;
import android.util.MutableBoolean;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputDevice;
@@ -2099,6 +2109,32 @@
}
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ID, mDisplayId);
+ for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
+ final TaskStack stack = mTaskStackContainers.get(stackNdx);
+ stack.writeToProto(proto, STACKS);
+ }
+ mDividerControllerLocked.writeToProto(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
+ mPinnedStackControllerLocked.writeToProto(proto, PINNED_STACK_CONTROLLER);
+ for (int i = mAboveAppWindowsContainers.size() - 1; i >= 0; --i) {
+ final WindowToken windowToken = mAboveAppWindowsContainers.get(i);
+ windowToken.writeToProto(proto, ABOVE_APP_WINDOWS);
+ }
+ for (int i = mBelowAppWindowsContainers.size() - 1; i >= 0; --i) {
+ final WindowToken windowToken = mBelowAppWindowsContainers.get(i);
+ windowToken.writeToProto(proto, BELOW_APP_WINDOWS);
+ }
+ for (int i = mImeWindowsContainers.size() - 1; i >= 0; --i) {
+ final WindowToken windowToken = mImeWindowsContainers.get(i);
+ windowToken.writeToProto(proto, IME_WINDOWS);
+ }
+ proto.write(DPI, mBaseDisplayDensity);
+ mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
@@ -3204,6 +3240,19 @@
mService.requestTraversal();
}
+ boolean okToDisplay() {
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ return !mService.mDisplayFrozen
+ && mService.mDisplayEnabled && mService.mPolicy.isScreenOn();
+ }
+ return mDisplayInfo.state == Display.STATE_ON;
+ }
+
+ boolean okToAnimate() {
+ return okToDisplay() &&
+ (mDisplayId != DEFAULT_DISPLAY || mService.mPolicy.okToAnimate());
+ }
+
static final class TaskForResizePointSearchResult {
boolean searchDone;
Task taskForResize;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6b51455..2b45d67 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -34,6 +34,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
+import static com.android.server.wm.proto.DockedStackDividerControllerProto.MINIMIZED_DOCK;
import android.content.Context;
import android.content.res.Configuration;
@@ -42,6 +43,7 @@
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.IDockedStackListener;
import android.view.animation.AnimationUtils;
@@ -919,4 +921,10 @@
mDimLayer.printTo(prefix + " ", pw);
}
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(MINIMIZED_DOCK, mMinimizedDock);
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 6d33ce2..1e7140a 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -21,6 +21,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.PinnedStackControllerProto.DEFAULT_BOUNDS;
+import static com.android.server.wm.proto.PinnedStackControllerProto.MOVEMENT_BOUNDS;
import android.app.RemoteAction;
import android.content.pm.ParceledListSlice;
@@ -35,6 +37,7 @@
import android.util.Size;
import android.util.Slog;
import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IPinnedStackController;
@@ -505,4 +508,12 @@
pw.println(prefix + " ]");
}
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS);
+ mService.getStackBounds(PINNED_STACK_ID, mTmpRect);
+ getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e83b792..05ef1a5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -35,11 +35,11 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
-import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.server.EventLogTags;
@@ -89,6 +89,8 @@
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAYS;
+import static com.android.server.wm.proto.WindowManagerServiceProto.WINDOWS;
/** Root {@link WindowContainer} for the device. */
class RootWindowContainer extends WindowContainer<DisplayContent> {
@@ -1055,6 +1057,19 @@
}
}
+ void writeToProto(ProtoOutputStream proto) {
+ if (mService.mDisplayReady) {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; ++i) {
+ final DisplayContent displayContent = mChildren.get(i);
+ displayContent.writeToProto(proto, DISPLAYS);
+ }
+ }
+ forAllWindows((w) -> {
+ w.writeIdentifierToProto(proto, WINDOWS);
+ }, true);
+ }
+
@Override
String getName() {
return "ROOT";
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
index ac06c6d..3603f2f 100644
--- a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
+++ b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
@@ -29,12 +29,16 @@
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT;
/**
- * SurfaceControl extension that has background sized to match its container.
+ * SurfaceControl extension that has black background behind navigation bar area for fullscreen
+ * letterboxed apps.
*/
class SurfaceControlWithBackground extends SurfaceControl {
- // SurfaceControl that holds the background behind opaque letterboxed app windows.
+ // SurfaceControl that holds the background.
private SurfaceControl mBackgroundControl;
// Flag that defines whether the background should be shown.
@@ -51,8 +55,10 @@
private float mLastDsDx = 1, mLastDsDy = 1;
private float mLastX, mLastY;
- // Will skip alpha animation for background of starting window.
- private boolean mIsStartingWindow;
+ // SurfaceFlinger doesn't support crop rectangles where width or height is non-positive.
+ // If we just set an empty crop it will behave as if there is no crop at all.
+ // To fix this we explicitly hide the surface and won't let it to be shown.
+ private boolean mHiddenForCrop = false;
public SurfaceControlWithBackground(SurfaceControlWithBackground other) {
super(other);
@@ -76,7 +82,6 @@
mLastWidth = w;
mLastHeight = h;
mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- mIsStartingWindow = windowType == TYPE_APPLICATION_STARTING;
mBackgroundControl = new SurfaceControl(s, "Background for - " + name,
mTmpContainerRect.width(), mTmpContainerRect.height(), PixelFormat.OPAQUE,
flags | SurfaceControl.FX_SURFACE_DIM);
@@ -89,10 +94,7 @@
if (mBackgroundControl == null) {
return;
}
- // We won't animate alpha for starting window because it will be visible as a flash for user
- // when fading out to reveal real app window.
- final float backgroundAlpha = mIsStartingWindow && alpha < 1.f ? 0 : alpha;
- mBackgroundControl.setAlpha(backgroundAlpha);
+ mBackgroundControl.setAlpha(alpha);
}
@Override
@@ -146,15 +148,10 @@
if (mBackgroundControl == null) {
return;
}
- if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
- // We're animating and cropping window, compute the appropriate crop for background.
- calculateBgCrop(crop);
- mBackgroundControl.setWindowCrop(mTmpContainerRect);
- } else {
- // When not animating just set crop to container rect.
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- mBackgroundControl.setWindowCrop(mTmpContainerRect);
- }
+ calculateBgCrop(crop);
+ mBackgroundControl.setWindowCrop(mTmpContainerRect);
+ mHiddenForCrop = mTmpContainerRect.isEmpty();
+ updateBackgroundVisibility();
}
@Override
@@ -164,34 +161,24 @@
if (mBackgroundControl == null) {
return;
}
- if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
- // We're animating and cropping window, compute the appropriate crop for background.
- calculateBgCrop(crop);
- mBackgroundControl.setFinalCrop(mTmpContainerRect);
- } else {
- // When not animating just set crop to container rect.
- mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- mBackgroundControl.setFinalCrop(mTmpContainerRect);
- }
+ mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+ mBackgroundControl.setFinalCrop(mTmpContainerRect);
}
- /** Compute background crop based on current animation progress for main surface control. */
+ /**
+ * Compute background crop based on current animation progress for main surface control and
+ * update {@link #mTmpContainerRect} with new values.
+ */
private void calculateBgCrop(Rect crop) {
// Track overall progress of animation by computing cropped portion of status bar.
final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets;
float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top;
if (d > 1.f) {
// We're running expand animation from launcher, won't compute custom bg crop here.
- mTmpContainerRect.set(crop);
+ mTmpContainerRect.setEmpty();
return;
}
- // Compute additional offset for the background when app window is positioned not at (0,0).
- // E.g. landscape with navigation bar on the left.
- final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
- final int offsetX = (int) (winFrame.left * mLastDsDx * d + 0.5);
- final int offsetY = (int) (winFrame.top * mLastDsDy * d + 0.5);
-
// Compute new scaled width and height for background that will depend on current animation
// progress. Those consist of current crop rect for the main surface + scaled areas outside
// of letterboxed area.
@@ -201,17 +188,39 @@
// computing correct frames for letterboxed windows in WindowState.
d = d < 0.025f ? 0 : d;
mWindowSurfaceController.getContainerRect(mTmpContainerRect);
- final int backgroundWidth =
- (int) (crop.width() + (mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
- final int backgroundHeight =
- (int) (crop.height() + (mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5);
+ int backgroundWidth = 0, backgroundHeight = 0;
+ // Compute additional offset for the background when app window is positioned not at (0,0).
+ // E.g. landscape with navigation bar on the left.
+ final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
+ int offsetX = (int)((winFrame.left - mTmpContainerRect.left) * mLastDsDx),
+ offsetY = (int) ((winFrame.top - mTmpContainerRect.top) * mLastDsDy);
- mTmpContainerRect.set(crop);
- // Make sure that part of background to left/top is visible and scaled.
- mTmpContainerRect.offset(offsetX, offsetY);
- // Set correct width/height, so that area to right/bottom is cropped properly.
- mTmpContainerRect.right = mTmpContainerRect.left + backgroundWidth;
- mTmpContainerRect.bottom = mTmpContainerRect.top + backgroundHeight;
+ // Position and size background.
+ final int bgPosition = mWindowSurfaceController.mAnimator.mService.getNavBarPosition();
+
+ switch (bgPosition) {
+ case NAV_BAR_LEFT:
+ backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
+ backgroundHeight = crop.height();
+ offsetX += crop.left - backgroundWidth;
+ offsetY += crop.top;
+ break;
+ case NAV_BAR_RIGHT:
+ backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
+ backgroundHeight = crop.height();
+ offsetX += crop.right;
+ offsetY += crop.top;
+ break;
+ case NAV_BAR_BOTTOM:
+ backgroundWidth = crop.width();
+ backgroundHeight = (int) ((mTmpContainerRect.height() - mLastHeight) * (1 - d)
+ + 0.5);
+ offsetX += crop.left;
+ offsetY += crop.bottom;
+ break;
+ }
+ mTmpContainerRect.set(offsetX, offsetY, offsetX + backgroundWidth,
+ offsetY + backgroundHeight);
}
@Override
@@ -317,7 +326,7 @@
return;
}
final AppWindowToken appWindowToken = mWindowSurfaceController.mAnimator.mWin.mAppToken;
- if (appWindowToken != null && appWindowToken.fillsParent() && mVisible) {
+ if (!mHiddenForCrop && mVisible && appWindowToken != null && appWindowToken.fillsParent()) {
mBackgroundControl.show();
} else {
mBackgroundControl.hide();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e5055e9..6349b05 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,6 +29,11 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.TaskProto.APP_WINDOW_TOKENS;
+import static com.android.server.wm.proto.TaskProto.BOUNDS;
+import static com.android.server.wm.proto.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.proto.TaskProto.ID;
+import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS;
import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskDescription;
@@ -37,11 +42,11 @@
import android.graphics.Rect;
import android.util.EventLog;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -735,6 +740,19 @@
return "Task=" + mTaskId;
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ID, mTaskId);
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final AppWindowToken appWindowToken = mChildren.get(i);
+ appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS);
+ }
+ proto.write(FILLS_PARENT, mFillsParent);
+ mBounds.writeToProto(proto, BOUNDS);
+ mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
final String doublePrefix = prefix + " ";
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 940ad33..60e6c7c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -204,7 +204,7 @@
}
final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
-1, -1, false, 1.0f, false, true);
- if (buffer == null) {
+ if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
return null;
}
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 39878cc..474edaa 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -36,6 +36,11 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
+import static com.android.server.wm.proto.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
+import static com.android.server.wm.proto.StackProto.BOUNDS;
+import static com.android.server.wm.proto.StackProto.FILLS_PARENT;
+import static com.android.server.wm.proto.StackProto.ID;
+import static com.android.server.wm.proto.StackProto.TASKS;
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
@@ -45,6 +50,7 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -52,7 +58,6 @@
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.internal.policy.DockedDividerUtils;
import com.android.server.EventLogTags;
-import com.android.server.UiThread;
import java.io.PrintWriter;
@@ -1225,6 +1230,18 @@
return mMinimizeAmount != 0f;
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(ID, mStackId);
+ for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
+ mChildren.get(taskNdx).writeToProto(proto, TASKS);
+ }
+ proto.write(FILLS_PARENT, mFillsParent);
+ mBounds.writeToProto(proto, BOUNDS);
+ proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "mStackId=" + mStackId);
pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5c664c2..86d8dd2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -102,6 +102,14 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.proto.WindowManagerServiceProto.APP_TRANSITION;
+import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAY_FROZEN;
+import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_APP;
+import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW;
+import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION;
+import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY;
+import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION;
import android.Manifest;
import android.Manifest.permission;
@@ -175,6 +183,7 @@
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
import android.view.Display;
import android.view.DisplayInfo;
@@ -2347,7 +2356,7 @@
// artifacts when we unfreeze the display if some different animation
// is running.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
- if (okToAnimate()) {
+ if (atoken.okToAnimate()) {
final DisplayContent displayContent = atoken.getTask().getDisplayContent();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int width = displayInfo.appWidth;
@@ -2425,14 +2434,6 @@
return false;
}
- boolean okToDisplay() {
- return !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn();
- }
-
- boolean okToAnimate() {
- return okToDisplay() && mPolicy.okToAnimate();
- }
-
@Override
public void addWindowToken(IBinder binder, int type, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
@@ -2699,7 +2700,9 @@
synchronized(mWindowMap) {
boolean prepared = mAppTransition.prepareAppTransitionLocked(transit, alwaysKeepCurrent,
flags, forceOverride);
- if (prepared && okToAnimate()) {
+ // TODO (multidisplay): associate app transitions with displays
+ final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+ if (prepared && dc != null && dc.okToAnimate()) {
mSkipAppTransitionAnimation = false;
}
}
@@ -5817,7 +5820,10 @@
// If the screen is currently frozen or off, then keep
// it frozen/off until this window draws at its new
// orientation.
- if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+ // TODO (multidisplay): Support screen freezing on secondary displays.
+ final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+ if ((dc == null || !dc.okToDisplay())
+ && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + w);
w.setOrientationChanging(true);
w.mLastFreezeDuration = 0;
@@ -6033,7 +6039,7 @@
return;
}
- if (!displayContent.isReady() || !mPolicy.isScreenOn() || !okToAnimate()) {
+ if (!displayContent.isReady() || !mPolicy.isScreenOn() || !displayContent.okToAnimate()) {
// No need to freeze the screen before the display is ready, if the screen is off,
// or we can't currently animate.
return;
@@ -6524,6 +6530,25 @@
}
}
+ private void writeToProtoLocked(ProtoOutputStream proto) {
+ mPolicy.writeToProto(proto, POLICY);
+ mRoot.writeToProto(proto);
+ if (mCurrentFocus != null) {
+ mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
+ }
+ if (mFocusedApp != null) {
+ mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+ }
+ if (mInputMethodWindow != null) {
+ mInputMethodWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
+ }
+ proto.write(DISPLAY_FROZEN, mDisplayFrozen);
+ final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
+ proto.write(ROTATION, defaultDisplayContent.getRotation());
+ proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
+ mAppTransition.writeToProto(proto, APP_TRANSITION);
+ }
+
private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
@@ -6798,6 +6823,7 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
boolean dumpAll = false;
+ boolean useProto = false;
int opti = 0;
while (opti < args.length) {
@@ -6808,6 +6834,8 @@
opti++;
if ("-a".equals(opt)) {
dumpAll = true;
+ } else if ("--proto".equals(opt)) {
+ useProto = true;
} else if ("-h".equals(opt)) {
pw.println("Window manager dump options:");
pw.println(" [-a] [-h] [cmd] ...");
@@ -6827,12 +6855,21 @@
pw.println(" \"visible\" for the visible windows.");
pw.println(" \"visible-apps\" for the visible app windows.");
pw.println(" -a: include all available server state.");
+ pw.println(" --proto: output dump in protocol buffer format.");
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
}
+ if (useProto) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ synchronized (mWindowMap) {
+ writeToProtoLocked(proto);
+ }
+ proto.flush();
+ return;
+ }
// Is the caller requesting to dump a particular piece of data?
if (opti < args.length) {
String cmd = args[opti];
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 12abe39..98bd25e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -102,6 +102,23 @@
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.proto.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.proto.IdentifierProto.TITLE;
+import static com.android.server.wm.proto.IdentifierProto.USER_ID;
+import static com.android.server.wm.proto.WindowStateProto.ANIMATING_EXIT;
+import static com.android.server.wm.proto.WindowStateProto.ANIMATOR;
+import static com.android.server.wm.proto.WindowStateProto.ATTRIBUTES;
+import static com.android.server.wm.proto.WindowStateProto.CHILD_WINDOWS;
+import static com.android.server.wm.proto.WindowStateProto.CONTAINING_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.CONTENT_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.CONTENT_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.DISPLAY_ID;
+import static com.android.server.wm.proto.WindowStateProto.FRAME;
+import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
+import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
+import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
import android.app.AppOpsManager;
import android.content.Context;
@@ -125,6 +142,7 @@
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
@@ -1711,7 +1729,7 @@
final boolean adjustedForMinimizedDockOrIme = task != null
&& (task.mStack.isAdjustedForMinimizedDockedStack()
|| task.mStack.isAdjustedForIme());
- if (mService.okToAnimate()
+ if (mToken.okToAnimate()
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& !isDragResizing() && !adjustedForMinimizedDockOrIme
&& (task == null || getTask().mStack.hasMovementAnimations())
@@ -1880,7 +1898,7 @@
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
- if (mHasSurface && mService.okToAnimate()) {
+ if (mHasSurface && mToken.okToAnimate()) {
if (mWillReplaceWindow) {
// This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
@@ -2315,7 +2333,7 @@
mLayoutNeeded = true;
}
- if (isDrawnLw() && mService.okToAnimate()) {
+ if (isDrawnLw() && mToken.okToAnimate()) {
mWinAnimator.applyEnterAnimationLocked();
}
}
@@ -2471,7 +2489,7 @@
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
+ mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
- if (!mService.okToAnimate()) {
+ if (!mToken.okToAnimate()) {
doAnimation = false;
} else if (mPolicyVisibility && mWinAnimator.mAnimation == null) {
// Check for the case where we are currently visible and
@@ -2501,7 +2519,7 @@
boolean hideLw(boolean doAnimation, boolean requestAnim) {
if (doAnimation) {
- if (!mService.okToAnimate()) {
+ if (!mToken.okToAnimate()) {
doAnimation = false;
}
}
@@ -3390,6 +3408,38 @@
|| (isChildWindow() && getParentWindow().isDockedResizing());
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ writeIdentifierToProto(proto, IDENTIFIER);
+ proto.write(DISPLAY_ID, getDisplayId());
+ proto.write(STACK_ID, getStackId());
+ mAttrs.writeToProto(proto, ATTRIBUTES);
+ mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
+ mFrame.writeToProto(proto, FRAME);
+ mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
+ mParentFrame.writeToProto(proto, PARENT_FRAME);
+ mContentFrame.writeToProto(proto, CONTENT_FRAME);
+ mContentInsets.writeToProto(proto, CONTENT_INSETS);
+ mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
+ mWinAnimator.writeToProto(proto, ANIMATOR);
+ proto.write(ANIMATING_EXIT, mAnimatingExit);
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).writeToProto(proto, CHILD_WINDOWS);
+ }
+ proto.end(token);
+ }
+
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, UserHandle.getUserId(mOwnerUid));
+ final CharSequence title = getWindowTag();
+ if (title != null) {
+ proto.write(TITLE, title.toString());
+ }
+ proto.end(token);
+ }
+
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final TaskStack stack = getStack();
pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86265c29..9d3db7c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -46,6 +46,8 @@
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.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
+import static com.android.server.wm.proto.WindowStateAnimatorProto.LAST_CLIP_RECT;
+import static com.android.server.wm.proto.WindowStateAnimatorProto.SURFACE;
import android.content.Context;
import android.graphics.Matrix;
@@ -57,6 +59,7 @@
import android.os.Debug;
import android.os.Trace;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.MagnificationSpec;
import android.view.Surface.OutOfResourcesException;
@@ -369,7 +372,7 @@
// we just started or just stopped animating by comparing mWasAnimating with isAnimationSet().
mWasAnimating = mAnimating;
final DisplayContent displayContent = mWin.getDisplayContent();
- if (displayContent != null && mService.okToAnimate()) {
+ if (mWin.mToken.okToAnimate()) {
// We will run animations as long as the display isn't frozen.
if (mWin.isDrawnLw() && mAnimation != null) {
@@ -1812,7 +1815,7 @@
// artifacts when we unfreeze the display if some different animation
// is running.
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
- if (mService.okToAnimate()) {
+ if (mWin.mToken.okToAnimate()) {
int anim = mPolicy.selectAnimationLw(mWin, transit);
int attr = -1;
Animation a = null;
@@ -1885,6 +1888,15 @@
mAnimation = newAnimation;
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mLastClipRect.writeToProto(proto, LAST_CLIP_RECT);
+ if (mSurfaceController != null) {
+ mSurfaceController.writeToProto(proto, SURFACE);
+ }
+ proto.end(token);
+ }
+
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
if (mAnimating || mLocalAnimating || mAnimationIsEntrance
|| mAnimation != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 110d5cb..2e1e3f7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -24,7 +26,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+import static com.android.server.wm.proto.WindowSurfaceControllerProto.LAYER;
+import static com.android.server.wm.proto.WindowSurfaceControllerProto.SHOWN;
import android.graphics.Point;
import android.graphics.PointF;
@@ -33,6 +36,7 @@
import android.os.IBinder;
import android.os.Debug;
import android.os.Trace;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -534,6 +538,12 @@
return mSurfaceH;
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(SHOWN, mSurfaceShown);
+ proto.write(LAYER, mSurfaceLayer);
+ proto.end(token);
+ }
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
if (dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e3033c9..422615b 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
+import android.util.proto.ProtoOutputStream;
import java.util.Comparator;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -26,6 +26,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE;
+import static com.android.server.wm.proto.WindowTokenProto.WINDOWS;
import android.os.Debug;
import android.os.IBinder;
@@ -261,6 +263,16 @@
super.onDisplayChanged(dc);
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ for (int i = 0; i < mChildren.size(); i++) {
+ final WindowState w = mChildren.get(i);
+ w.writeToProto(proto, WINDOWS);
+ }
+ proto.end(token);
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("windows="); pw.println(mChildren);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
@@ -288,4 +300,12 @@
String getName() {
return toString();
}
+
+ boolean okToDisplay() {
+ return mDisplayContent != null && mDisplayContent.okToDisplay();
+ }
+
+ boolean okToAnimate() {
+ return mDisplayContent != null && mDisplayContent.okToAnimate();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ab919a2..eb9683e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -99,6 +99,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.StringParceledListSlice;
@@ -9572,6 +9573,9 @@
< android.os.Build.VERSION_CODES.M) {
return false;
}
+ if (!isRuntimePermission(permission)) {
+ return false;
+ }
final PackageManager packageManager = mInjector.getPackageManager();
switch (grantState) {
case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
@@ -9598,6 +9602,8 @@
return true;
} catch (SecurityException se) {
return false;
+ } catch (NameNotFoundException e) {
+ return false;
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -9647,6 +9653,13 @@
}
}
+ public boolean isRuntimePermission(String permissionName) throws NameNotFoundException {
+ final PackageManager packageManager = mInjector.getPackageManager();
+ PermissionInfo permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+ return (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
+
@Override
public boolean isProvisioningAllowed(String action, String packageName) {
Preconditions.checkNotNull(packageName);
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
index bd65f57..8242149 100644
--- a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
@@ -38,6 +38,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.os.Build;
import android.os.IBinder;
import android.os.IInterface;
import android.os.UserHandle;
@@ -146,19 +147,86 @@
service.onSettingRestored(
service.getConfig().secureSettingName,
mExpectedPrimary.get(approvalLevel).get(userId),
- userId);
+ Build.VERSION_CODES.O, userId);
}
verifyExpectedApprovedEntries(service, true);
for (int userId : mExpectedSecondary.get(approvalLevel).keySet()) {
service.onSettingRestored(service.getConfig().secondarySettingName,
- mExpectedSecondary.get(approvalLevel).get(userId), userId);
+ mExpectedSecondary.get(approvalLevel).get(userId), Build.VERSION_CODES.O,
+ userId);
}
verifyExpectedApprovedEntries(service);
}
}
@Test
+ public void testBackupAndRestore_migration_preO() throws Exception {
+ ArrayMap backupPrimaryPackages = new ArrayMap<>();
+ backupPrimaryPackages.put(0, "backup.0:backup:0a");
+ backupPrimaryPackages.put(10, "10.backup");
+ backupPrimaryPackages.put(11, "eleven");
+ backupPrimaryPackages.put(12, "");
+ ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+ backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
+ backupPrimaryComponentNames.put(10, "again/M1");
+ backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
+ backupPrimaryComponentNames.put(12, "");
+ ArrayMap<Integer, ArrayMap<Integer, String>> backupPrimary = new ArrayMap<>();
+ backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
+ backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
+
+ ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+ backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
+ backupSecondaryComponentNames.put(10,
+ "this.is.another.package.backup/with.Component:component.backup/2");
+ ArrayMap backupSecondaryPackages = new ArrayMap<>();
+ backupSecondaryPackages.put(0, "");
+ backupSecondaryPackages.put(10,
+ "this.is.another.package.backup:package.backup");
+ ArrayMap<Integer, ArrayMap<Integer, String>> backupSecondary = new ArrayMap<>();
+ backupSecondary.put(APPROVAL_BY_PACKAGE, backupSecondaryPackages);
+ backupSecondary.put(APPROVAL_BY_COMPONENT, backupSecondaryComponentNames);
+
+ for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, approvalLevel);
+
+ // not an expected flow but a way to get data into the settings
+ for (int userId : mExpectedPrimary.get(approvalLevel).keySet()) {
+ service.onSettingRestored(
+ service.getConfig().secureSettingName,
+ mExpectedPrimary.get(approvalLevel).get(userId),
+ Build.VERSION_CODES.O, userId);
+ }
+
+ for (int userId : mExpectedSecondary.get(approvalLevel).keySet()) {
+ service.onSettingRestored(service.getConfig().secondarySettingName,
+ mExpectedSecondary.get(approvalLevel).get(userId), Build.VERSION_CODES.O,
+ userId);
+ }
+
+ // actual test
+ for (int userId : backupPrimary.get(approvalLevel).keySet()) {
+ service.onSettingRestored(
+ service.getConfig().secureSettingName,
+ backupPrimary.get(approvalLevel).get(userId),
+ Build.VERSION_CODES.N_MR1, userId);
+ }
+ verifyExpectedApprovedEntries(service, true);
+
+ for (int userId : backupSecondary.get(approvalLevel).keySet()) {
+ service.onSettingRestored(service.getConfig().secondarySettingName,
+ backupSecondary.get(approvalLevel).get(userId),
+ Build.VERSION_CODES.N_MR1, userId);
+ }
+ verifyExpectedApprovedEntries(service);
+ verifyExpectedApprovedEntries(service, backupPrimary.get(approvalLevel));
+ verifyExpectedApprovedEntries(service, backupSecondary.get(approvalLevel));
+ }
+ }
+
+ @Test
public void testReadXml_migrationFromSettings() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -619,25 +687,22 @@
ArrayMap<Integer, String> verifyMap = primary
? mExpectedPrimary.get(service.mApprovalLevel)
: mExpectedSecondary.get(service.mApprovalLevel);
+ verifyExpectedApprovedEntries(service, verifyMap);
+ }
+
+ private void verifyExpectedApprovedEntries(ManagedServices service,
+ ArrayMap<Integer, String> verifyMap) {
for (int userId : verifyMap.keySet()) {
for (String verifyValue : verifyMap.get(userId).split(":")) {
if (!TextUtils.isEmpty(verifyValue)) {
assertTrue("service type " + service.mApprovalLevel + ":"
- + verifyValue + " is not allowed for user " + userId,
+ + verifyValue + " is not allowed for user " + userId,
service.isPackageOrComponentAllowed(verifyValue, userId));
}
}
}
}
- private boolean isPackage(String packageOrComponent) {
- final ComponentName component = ComponentName.unflattenFromString(packageOrComponent);
- if (component != null) {
- return false;
- }
- return true;
- }
-
private void writeExpectedValuesToSettings(int approvalLevel) {
for (int userId : mExpectedPrimary.get(approvalLevel).keySet()) {
Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING,
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 48464e5..ba22159 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -78,7 +78,7 @@
service.mStackSupervisor.inResumeTopActivity = true;
testStack.mResumedActivity = activityRecord;
- final boolean waiting = testStack.checkReadyForSleepLocked();
+ final boolean waiting = testStack.goToSleepIfPossible(false);
// Ensure we report not being ready for sleep.
assertTrue(waiting);
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 04b5bde..4ad92c7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -218,6 +218,12 @@
return createTestStack(stackId, createOnTop);
}
+
+ // Always keep things awake
+ @Override
+ boolean hasAwakeDisplay() {
+ return true;
+ }
}
private static WindowManagerService prepareMockWindowManager() {
diff --git a/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java b/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java
index 0d29e89..9c010a0 100644
--- a/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java
@@ -17,10 +17,14 @@
package com.android.server.wallpaper;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.app.WallpaperColors;
+import android.os.Handler;
+import android.os.Message;
import android.os.SystemClock;
import android.service.wallpaper.WallpaperService;
+import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -28,18 +32,31 @@
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Supplier;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WallpaperServiceTests {
+ @UiThreadTest
@Test
public void testNotifyColorsChanged_rateLimit() throws Exception {
+ long[] clockOffset = {0};
+ boolean[] postDelayed = {false};
+ Supplier<Long> clockFunction = () -> SystemClock.elapsedRealtime() + clockOffset[0];
+ Handler handler = new Handler() {
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ postDelayed[0] = true;
+ return super.sendMessageAtTime(msg, uptimeMillis);
+ }
+ };
+
CountDownLatch eventCountdown = new CountDownLatch(2);
WallpaperService service = new WallpaperService() {
@Override
public Engine onCreateEngine() {
- return new WallpaperService.Engine() {
+ return new WallpaperService.Engine(clockFunction, handler) {
@Override
public WallpaperColors onComputeColors() {
eventCountdown.countDown();
@@ -59,8 +76,11 @@
engine.notifyColorsChanged();
assertEquals("OnComputeColors should have been throttled.",
1, eventCountdown.getCount());
- // Called after being deferred.
- engine.setClockFunction(() -> SystemClock.elapsedRealtime() + 1500);
+ // Should have been posted to the handler.
+ assertTrue("Event should have been delayed", postDelayed[0]);
+
+ // Called again after being deferred.
+ clockOffset[0] = 1500;
engine.notifyColorsChanged();
assertEquals("OnComputeColors should have been deferred.",
0, eventCountdown.getCount());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 95adc9c..9f57f49 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.doAnswer;
import android.os.PowerSaveState;
+import android.util.proto.ProtoOutputStream;
import org.mockito.invocation.InvocationOnMock;
import android.annotation.Nullable;
@@ -596,6 +597,11 @@
}
@Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+
+ }
+
+ @Override
public void dump(String prefix, PrintWriter writer, String[] args) {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 7ff1110..40c79bb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -92,7 +92,9 @@
boolean mOnTop = false;
TestAppWindowToken(DisplayContent dc) {
- super(dc.mService, new IApplicationToken.Stub() {}, false, dc, true /* fillsParent */,
+ super(dc.mService, new IApplicationToken.Stub() {
+ public String getName() {return null;}
+ }, false, dc, true /* fillsParent */,
null /* overrideConfig */, null /* bounds */);
}
@@ -287,6 +289,10 @@
public IBinder asBinder() {
return mBinder;
}
+ @Override
+ public String getName() {
+ return null;
+ }
}
/** Used to track resize reports. */
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5fb83ab..e448fb2 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -795,34 +795,57 @@
return rtString;
}
+ /**
+ * Convert RIL Service State to String
+ *
+ * @param serviceState
+ * @return String representation of the ServiceState
+ *
+ * @hide
+ */
+ public static String rilServiceStateToString(int serviceState) {
+ switch(serviceState) {
+ case STATE_IN_SERVICE:
+ return "IN_SERVICE";
+ case STATE_OUT_OF_SERVICE:
+ return "OUT_OF_SERVICE";
+ case STATE_EMERGENCY_ONLY:
+ return "EMERGENCY_ONLY";
+ case STATE_POWER_OFF:
+ return "POWER_OFF";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
@Override
public String toString() {
- String radioTechnology = rilRadioTechnologyToString(mRilVoiceRadioTechnology);
- String dataRadioTechnology = rilRadioTechnologyToString(mRilDataRadioTechnology);
-
- return (mVoiceRegState + " " + mDataRegState
- + " "
- + "voice " + getRoamingLogString(mVoiceRoamingType)
- + " "
- + "data " + getRoamingLogString(mDataRoamingType)
- + " " + mVoiceOperatorAlphaLong
- + " " + mVoiceOperatorAlphaShort
- + " " + mVoiceOperatorNumeric
- + " " + mDataOperatorAlphaLong
- + " " + mDataOperatorAlphaShort
- + " " + mDataOperatorNumeric
- + " " + (mIsManualNetworkSelection ? "(manual)" : "")
- + " " + radioTechnology
- + " " + dataRadioTechnology
- + " " + (mCssIndicator ? "CSS supported" : "CSS not supported")
- + " " + mNetworkId
- + " " + mSystemId
- + " RoamInd=" + mCdmaRoamingIndicator
- + " DefRoamInd=" + mCdmaDefaultRoamingIndicator
- + " EmergOnly=" + mIsEmergencyOnly
- + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration
- + " IsUsingCarrierAggregation=" + mIsUsingCarrierAggregation
- + " mLteEarfcnRsrpBoost=" + mLteEarfcnRsrpBoost);
+ return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
+ .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
+ .append(", mDataRegState=").append(mDataRegState)
+ .append("(" + rilServiceStateToString(mDataRegState) + ")")
+ .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
+ .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
+ .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
+ .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
+ .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
+ .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+ .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
+ .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
+ .append(", mRilVoiceRadioTechnology=").append(mRilVoiceRadioTechnology)
+ .append("(" + rilRadioTechnologyToString(mRilVoiceRadioTechnology) + ")")
+ .append(", mRilDataRadioTechnology=").append(mRilDataRadioTechnology)
+ .append("(" + rilRadioTechnologyToString(mRilDataRadioTechnology) + ")")
+ .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
+ .append(", mNetworkId=").append(mNetworkId)
+ .append(", mSystemId=").append(mSystemId)
+ .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
+ .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
+ .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
+ .append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration)
+ .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
+ .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+ .append("}").toString();
}
private void setNullState(int state) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index dcb9723..c4965a0 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -45,6 +46,7 @@
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.util.SharedLog;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.Looper;
import android.os.INetworkManagementService;
@@ -110,6 +112,12 @@
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
}
+ private void waitForIdle() {
+ ConditionVariable cv = new ConditionVariable();
+ new Handler(Looper.getMainLooper()).post(() -> { cv.open(); });
+ cv.block();
+ }
+
private OffloadController makeOffloadController() throws Exception {
OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
mHardware, mContentResolver, mNMService, new SharedLog("test"));
@@ -417,4 +425,68 @@
entry = stats.getValues(ethernetPosition, entry);
assertNetworkStats(ethernetIface, ethernetStats, entry);
}
+
+ @Test
+ public void testSetInterfaceQuota() throws Exception {
+ setupFunctioningHardwareInterface();
+ enableOffload();
+
+ final OffloadController offload = makeOffloadController();
+ offload.start();
+
+ final String ethernetIface = "eth1";
+ final String mobileIface = "rmnet_data0";
+ final long ethernetLimit = 12345;
+ final long mobileLimit = 12345678;
+
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(ethernetIface);
+ offload.setUpstreamLinkProperties(lp);
+
+ ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
+ final InOrder inOrder = inOrder(mHardware);
+ when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
+ when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
+
+ // Applying an interface quota to the current upstream immediately sends it to the hardware.
+ provider.setInterfaceQuota(ethernetIface, ethernetLimit);
+ waitForIdle();
+ inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
+ inOrder.verifyNoMoreInteractions();
+
+ // Applying an interface quota to another upstream does not take any immediate action.
+ provider.setInterfaceQuota(mobileIface, mobileLimit);
+ waitForIdle();
+ inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+
+ // Switching to that upstream causes the quota to be applied if the parameters were applied
+ // correctly.
+ lp.setInterfaceName(mobileIface);
+ offload.setUpstreamLinkProperties(lp);
+ waitForIdle();
+ inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
+
+ // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
+ // to Long.MAX_VALUE.
+ provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ waitForIdle();
+ inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
+
+ // If setting upstream parameters fails, then the data limit is not set.
+ when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
+ lp.setInterfaceName(ethernetIface);
+ offload.setUpstreamLinkProperties(lp);
+ provider.setInterfaceQuota(mobileIface, mobileLimit);
+ waitForIdle();
+ inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+
+ // If setting the data limit fails while changing upstreams, offload is stopped.
+ when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
+ when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
+ lp.setInterfaceName(mobileIface);
+ offload.setUpstreamLinkProperties(lp);
+ provider.setInterfaceQuota(mobileIface, mobileLimit);
+ waitForIdle();
+ inOrder.verify(mHardware).stopOffloadControl();
+ }
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 9005c5b..c25c4f6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -574,6 +574,11 @@
if (file_op.xml_to_flatten) {
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
LinkAndVersionXmlFile(table, &file_op);
+ if (versioned_docs.empty()) {
+ error = true;
+ continue;
+ }
+
for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
std::string dst_path = file_op.dst_path;
if (doc->file.config != file_op.config) {