Merge "Rename getDisplayAssignedToUser() to getMainDisplayAssignedToUser()."
diff --git a/Android.bp b/Android.bp
index 926b4d8..9cd43fa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -504,6 +504,13 @@
}
filegroup {
+ name: "framework-android-os-unit-testable-src",
+ srcs: [
+ "core/java/android/os/DdmSyncState.java",
+ ],
+}
+
+filegroup {
name: "framework-networkstack-shared-srcs",
srcs: [
// TODO: remove these annotations as soon as we can use andoid.support.annotations.*
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 89eb1a9..4477e94 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1259,10 +1259,14 @@
final BackgroundStartPrivileges bsp =
activityManagerInternal.getBackgroundStartPrivileges(uid);
- final boolean balAllowed = bsp.allowsBackgroundActivityStarts();
if (DEBUG) {
- Slog.d(TAG, "Job " + job.toShortString() + " bal state: " + bsp);
+ Slog.d(TAG, "Job " + job.toShortString() + " bsp state: " + bsp);
}
+ // Intentionally use the background activity start BSP here instead of
+ // the full BAL check since the former is transient and better indicates that the
+ // user recently interacted with the app, while the latter includes
+ // permanent exceptions that don't warrant bypassing normal concurrency policy.
+ final boolean balAllowed = bsp.allowsBackgroundActivityStarts();
cachedPrivilegedState.put(uid,
balAllowed ? PRIVILEGED_STATE_BAL : PRIVILEGED_STATE_NONE);
return balAllowed;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 08810b5..4cf9c8c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -31,7 +31,6 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
-import android.app.BackgroundStartPrivileges;
import android.app.IUidObserver;
import android.app.compat.CompatChanges;
import android.app.job.IJobScheduler;
@@ -3782,7 +3781,8 @@
return canPersist;
}
- private int validateJob(@NonNull JobInfo job, int callingUid, int sourceUserId,
+ private int validateJob(@NonNull JobInfo job, int callingUid, int callingPid,
+ int sourceUserId,
@Nullable String sourcePkgName, @Nullable JobWorkItem jobWorkItem) {
final boolean rejectNegativeNetworkEstimates = CompatChanges.isChangeEnabled(
JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES, callingUid);
@@ -3815,6 +3815,8 @@
}
// We aim to check the permission of both the source and calling app so that apps
// don't attempt to bypass the permission by using other apps to do the work.
+ boolean isInStateToScheduleUiJobSource = false;
+ final String callingPkgName = job.getService().getPackageName();
if (sourceUid != -1) {
// Check the permission of the source app.
final int sourceResult =
@@ -3822,8 +3824,13 @@
if (sourceResult != JobScheduler.RESULT_SUCCESS) {
return sourceResult;
}
+ final int sourcePid =
+ callingUid == sourceUid && callingPkgName.equals(sourcePkgName)
+ ? callingPid : -1;
+ isInStateToScheduleUiJobSource = isInStateToScheduleUserInitiatedJobs(
+ sourceUid, sourcePid, sourcePkgName);
}
- final String callingPkgName = job.getService().getPackageName();
+ boolean isInStateToScheduleUiJobCalling = false;
if (callingUid != sourceUid || !callingPkgName.equals(sourcePkgName)) {
// Source app is different from calling app. Make sure the calling app also has
// the permission.
@@ -3832,25 +3839,17 @@
if (callingResult != JobScheduler.RESULT_SUCCESS) {
return callingResult;
}
+ // Avoid rechecking the state if the source app is able to schedule the job.
+ if (!isInStateToScheduleUiJobSource) {
+ isInStateToScheduleUiJobCalling = isInStateToScheduleUserInitiatedJobs(
+ callingUid, callingPid, callingPkgName);
+ }
}
- final int uid = sourceUid != -1 ? sourceUid : callingUid;
- final int procState = mActivityManagerInternal.getUidProcessState(uid);
- if (DEBUG) {
- Slog.d(TAG, "Uid " + uid + " proc state="
- + ActivityManager.procStateToString(procState));
- }
- if (procState != ActivityManager.PROCESS_STATE_TOP) {
- final BackgroundStartPrivileges bsp =
- mActivityManagerInternal.getBackgroundStartPrivileges(uid);
- if (DEBUG) {
- Slog.d(TAG, "Uid " + uid + ": " + bsp);
- }
- if (!bsp.allowsBackgroundActivityStarts()) {
- Slog.e(TAG,
- "Uid " + uid + " not in a state to schedule user-initiated jobs");
- return JobScheduler.RESULT_FAILURE;
- }
+ if (!isInStateToScheduleUiJobSource && !isInStateToScheduleUiJobCalling) {
+ Slog.e(TAG, "Uid(s) " + sourceUid + "/" + callingUid
+ + " not in a state to schedule user-initiated jobs");
+ return JobScheduler.RESULT_FAILURE;
}
}
if (jobWorkItem != null) {
@@ -3896,6 +3895,24 @@
return JobScheduler.RESULT_SUCCESS;
}
+ private boolean isInStateToScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
+ final int procState = mActivityManagerInternal.getUidProcessState(uid);
+ if (DEBUG) {
+ Slog.d(TAG, "Uid " + uid + " proc state="
+ + ActivityManager.procStateToString(procState));
+ }
+ if (procState == ActivityManager.PROCESS_STATE_TOP) {
+ return true;
+ }
+ final boolean canScheduleUiJobsInBg =
+ mActivityManagerInternal.canScheduleUserInitiatedJobs(uid, pid, pkgName);
+ if (DEBUG) {
+ Slog.d(TAG, "Uid " + uid
+ + " AM.canScheduleUserInitiatedJobs= " + canScheduleUiJobsInBg);
+ }
+ return canScheduleUiJobsInBg;
+ }
+
// IJobScheduler implementation
@Override
public int schedule(String namespace, JobInfo job) throws RemoteException {
@@ -3908,7 +3925,7 @@
enforceValidJobRequest(uid, pid, job);
- final int result = validateJob(job, uid, -1, null, null);
+ final int result = validateJob(job, uid, pid, -1, null, null);
if (result != JobScheduler.RESULT_SUCCESS) {
return result;
}
@@ -3941,7 +3958,7 @@
throw new NullPointerException("work is null");
}
- final int result = validateJob(job, uid, -1, null, work);
+ final int result = validateJob(job, uid, pid, -1, null, work);
if (result != JobScheduler.RESULT_SUCCESS) {
return result;
}
@@ -3963,6 +3980,7 @@
public int scheduleAsPackage(String namespace, JobInfo job, String packageName, int userId,
String tag) throws RemoteException {
final int callerUid = Binder.getCallingUid();
+ final int callerPid = Binder.getCallingPid();
if (DEBUG) {
Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
+ " on behalf of " + packageName + "/");
@@ -3979,7 +3997,7 @@
+ " not permitted to schedule jobs for other apps");
}
- int result = validateJob(job, callerUid, userId, packageName, null);
+ int result = validateJob(job, callerUid, callerPid, userId, packageName, null);
if (result != JobScheduler.RESULT_SUCCESS) {
return result;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index ab0a8ad..55e6815 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -489,14 +489,6 @@
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- /**
- * Whether we should allow apps into the
- * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
- * If false, any attempts to put an app into the bucket will put the app into the
- * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE} bucket instead.
- */
- private boolean mAllowRestrictedBucket;
-
private volatile boolean mAppIdleEnabled;
private volatile boolean mIsCharging;
private boolean mSystemServicesReady = false;
@@ -1058,13 +1050,6 @@
Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
}
}
- if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
- newBucket = STANDBY_BUCKET_RARE;
- // Leave the reason alone.
- if (DEBUG) {
- Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
- }
- }
if (newBucket > minBucket) {
newBucket = minBucket;
// Leave the reason alone.
@@ -1689,7 +1674,7 @@
final int reason = (REASON_MAIN_MASK & mainReason) | (REASON_SUB_MASK & restrictReason);
final long nowElapsed = mInjector.elapsedRealtime();
- final int bucket = mAllowRestrictedBucket ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE;
+ final int bucket = STANDBY_BUCKET_RESTRICTED;
setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
}
@@ -1793,9 +1778,6 @@
Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
return;
}
- if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
- newBucket = STANDBY_BUCKET_RARE;
- }
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
@@ -1921,7 +1903,6 @@
+ " due to min timeout");
}
} else if (newBucket == STANDBY_BUCKET_RARE
- && mAllowRestrictedBucket
&& getBucketForLocked(packageName, userId, elapsedRealtime)
== STANDBY_BUCKET_RESTRICTED) {
// Prediction doesn't think the app will be used anytime soon and
@@ -2529,8 +2510,6 @@
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
- pw.print(" mAllowRestrictedBucket=");
- pw.print(mAllowRestrictedBucket);
pw.print(" mIsCharging=");
pw.print(mIsCharging);
pw.println();
@@ -2711,12 +2690,6 @@
}
}
- boolean isRestrictedBucketEnabled() {
- return Global.getInt(mContext.getContentResolver(),
- Global.ENABLE_RESTRICTED_BUCKET,
- Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
- }
-
File getDataSystemDirectory() {
return Environment.getDataSystemDirectory();
}
@@ -3079,11 +3052,6 @@
// APP_STANDBY_ENABLED is a SystemApi that some apps may be watching, so best to
// leave it in Settings.
cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
- // Leave ENABLE_RESTRICTED_BUCKET as a user-controlled setting which will stay in
- // Settings.
- // TODO: make setting user-specific
- cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
- false, this);
// ADAPTIVE_BATTERY_MANAGEMENT_ENABLED is a user setting, so it has to stay in Settings.
cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
false, this);
@@ -3284,10 +3252,6 @@
Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
}
- synchronized (mAppIdleLock) {
- mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled();
- }
-
setAppIdleEnabled(mInjector.isAppIdleEnabled());
}
diff --git a/api/Android.bp b/api/Android.bp
index 73dbd28..24b3004 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -41,23 +41,6 @@
}
python_binary_host {
- name: "api_versions_trimmer",
- srcs: ["api_versions_trimmer.py"],
-}
-
-python_test_host {
- name: "api_versions_trimmer_unittests",
- main: "api_versions_trimmer_unittests.py",
- srcs: [
- "api_versions_trimmer_unittests.py",
- "api_versions_trimmer.py",
- ],
- test_options: {
- unit_test: true,
- },
-}
-
-python_binary_host {
name: "merge_annotation_zips",
srcs: ["merge_annotation_zips.py"],
}
diff --git a/api/api.go b/api/api.go
index 9876abb..09c2383 100644
--- a/api/api.go
+++ b/api/api.go
@@ -194,55 +194,6 @@
}
}
-func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
- // For the filtered api versions, we prune all APIs except art module's APIs. because
- // 1) ART apis are available by default to all modules, while other module-to-module deps are
- // explicit and probably receive more scrutiny anyway
- // 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
- // 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
- // per-module lint databases that excludes just that module's APIs. Alas, that's more
- // difficult to achieve.
- modules = remove(modules, art)
-
- for _, i := range []struct{
- name string
- out string
- in string
- }{
- {
- // We shouldn't need public-filtered or system-filtered.
- // public-filtered is currently used to lint things that
- // use the module sdk or the system server sdk, but those
- // should be switched over to module-filtered and
- // system-server-filtered, and then public-filtered can
- // be removed.
- name: "api-versions-xml-public-filtered",
- out: "api-versions-public-filtered.xml",
- in: ":api_versions_public{.api_versions.xml}",
- }, {
- name: "api-versions-xml-module-lib-filtered",
- out: "api-versions-module-lib-filtered.xml",
- in: ":api_versions_module_lib{.api_versions.xml}",
- }, {
- name: "api-versions-xml-system-server-filtered",
- out: "api-versions-system-server-filtered.xml",
- in: ":api_versions_system_server{.api_versions.xml}",
- },
- } {
- props := genruleProps{}
- props.Name = proptools.StringPtr(i.name)
- props.Out = []string{i.out}
- // Note: order matters: first parameter is the full api-versions.xml
- // after that the stubs files in any order
- // stubs files are all modules that export API surfaces EXCEPT ART
- props.Srcs = append([]string{i.in}, createSrcs(modules, ".stubs{.jar}")...)
- props.Tools = []string{"api_versions_trimmer"}
- props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
- props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
- ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable)
- }
-}
-
func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs")
@@ -395,8 +346,6 @@
createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
- createFilteredApiVersions(ctx, bootclasspath)
-
createPublicStubsSourceFilegroup(ctx, bootclasspath)
}
diff --git a/api/api_versions_trimmer.py b/api/api_versions_trimmer.py
deleted file mode 100755
index 9afd95a..0000000
--- a/api/api_versions_trimmer.py
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-
-"""Script to remove mainline APIs from the api-versions.xml."""
-
-import argparse
-import re
-import xml.etree.ElementTree as ET
-import zipfile
-
-
-def read_classes(stubs):
- """Read classes from the stubs file.
-
- Args:
- stubs: argument can be a path to a file (a string), a file-like object or a
- path-like object
-
- Returns:
- a set of the classes found in the file (set of strings)
- """
- classes = set()
- with zipfile.ZipFile(stubs) as z:
- for info in z.infolist():
- if (not info.is_dir()
- and info.filename.endswith(".class")
- and not info.filename.startswith("META-INF")):
- # drop ".class" extension
- classes.add(info.filename[:-6])
- return classes
-
-
-def filter_method_tag(method, classes_to_remove):
- """Updates the signature of this method by calling filter_method_signature.
-
- Updates the method passed into this function.
-
- Args:
- method: xml element that represents a method
- classes_to_remove: set of classes you to remove
- """
- filtered = filter_method_signature(method.get("name"), classes_to_remove)
- method.set("name", filtered)
-
-
-def filter_method_signature(signature, classes_to_remove):
- """Removes mentions of certain classes from this method signature.
-
- Replaces any existing classes that need to be removed, with java/lang/Object
-
- Args:
- signature: string that is a java representation of a method signature
- classes_to_remove: set of classes you to remove
- """
- regex = re.compile("L.*?;")
- start = signature.find("(")
- matches = set(regex.findall(signature[start:]))
- for m in matches:
- # m[1:-1] to drop the leading `L` and `;` ending
- if m[1:-1] in classes_to_remove:
- signature = signature.replace(m, "Ljava/lang/Object;")
- return signature
-
-
-def filter_lint_database(database, classes_to_remove, output):
- """Reads a lint database and writes a filtered version without some classes.
-
- Reads database from api-versions.xml and removes any references to classes
- in the second argument. Writes the result (another xml with the same format
- of the database) to output.
-
- Args:
- database: path to xml with lint database to read
- classes_to_remove: iterable (ideally a set or similar for quick
- lookups) that enumerates the classes that should be removed
- output: path to write the filtered database
- """
- xml = ET.parse(database)
- root = xml.getroot()
- for c in xml.findall("class"):
- cname = c.get("name")
- if cname in classes_to_remove:
- root.remove(c)
- else:
- # find the <extends /> tag inside this class to see if the parent
- # has been removed from the known classes (attribute called name)
- super_classes = c.findall("extends")
- for super_class in super_classes:
- super_class_name = super_class.get("name")
- if super_class_name in classes_to_remove:
- super_class.set("name", "java/lang/Object")
- interfaces = c.findall("implements")
- for interface in interfaces:
- interface_name = interface.get("name")
- if interface_name in classes_to_remove:
- c.remove(interface)
- for method in c.findall("method"):
- filter_method_tag(method, classes_to_remove)
- xml.write(output)
-
-
-def main():
- """Run the program."""
- parser = argparse.ArgumentParser(
- description=
- ("Read a lint database (api-versions.xml) and many stubs jar files. "
- "Produce another database file that doesn't include the classes present "
- "in the stubs file(s)."))
- parser.add_argument("output", help="Destination of the result (xml file).")
- parser.add_argument(
- "api_versions",
- help="The lint database (api-versions.xml file) to read data from"
- )
- parser.add_argument("stubs", nargs="+", help="The stubs jar file(s)")
- parsed = parser.parse_args()
- classes = set()
- for stub in parsed.stubs:
- classes.update(read_classes(stub))
- filter_lint_database(parsed.api_versions, classes, parsed.output)
-
-
-if __name__ == "__main__":
- main()
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py
deleted file mode 100644
index d2e5b7d..0000000
--- a/api/api_versions_trimmer_unittests.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-
-import io
-import re
-import unittest
-import xml.etree.ElementTree as ET
-import zipfile
-
-import api_versions_trimmer
-
-
-def create_in_memory_zip_file(files):
- f = io.BytesIO()
- with zipfile.ZipFile(f, "w") as z:
- for fname in files:
- with z.open(fname, mode="w") as class_file:
- class_file.write(b"")
- return f
-
-
-def indent(elem, level=0):
- i = "\n" + level * " "
- j = "\n" + (level - 1) * " "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- for subelem in elem:
- indent(subelem, level + 1)
- if not elem.tail or not elem.tail.strip():
- elem.tail = j
- else:
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = j
- return elem
-
-
-def pretty_print(s):
- tree = ET.parse(io.StringIO(s))
- el = indent(tree.getroot())
- res = ET.tostring(el).decode("utf-8")
- # remove empty lines inside the result because this still breaks some
- # comparisons
- return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE)
-
-
-class ApiVersionsTrimmerUnittests(unittest.TestCase):
-
- def setUp(self):
- # so it prints diffs in long strings (xml files)
- self.maxDiff = None
-
- def test_read_classes(self):
- f = create_in_memory_zip_file(
- ["a/b/C.class",
- "a/b/D.class",
- ]
- )
- res = api_versions_trimmer.read_classes(f)
- self.assertEqual({"a/b/C", "a/b/D"}, res)
-
- def test_read_classes_ignore_dex(self):
- f = create_in_memory_zip_file(
- ["a/b/C.class",
- "a/b/D.class",
- "a/b/E.dex",
- "f.dex",
- ]
- )
- res = api_versions_trimmer.read_classes(f)
- self.assertEqual({"a/b/C", "a/b/D"}, res)
-
- def test_read_classes_ignore_manifest(self):
- f = create_in_memory_zip_file(
- ["a/b/C.class",
- "a/b/D.class",
- "META-INFO/G.class"
- ]
- )
- res = api_versions_trimmer.read_classes(f)
- self.assertEqual({"a/b/C", "a/b/D"}, res)
-
- def test_filter_method_signature(self):
- xml = """
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
- """
- method = ET.fromstring(xml)
- classes_to_remove = {"android/accessibilityservice/GestureDescription"}
- expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
- api_versions_trimmer.filter_method_tag(method, classes_to_remove)
- self.assertEqual(expected, method.get("name"))
-
- def test_filter_method_signature_with_L_in_method(self):
- xml = """
- <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
- """
- method = ET.fromstring(xml)
- classes_to_remove = {"android/accessibilityservice/GestureDescription"}
- expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
- api_versions_trimmer.filter_method_tag(method, classes_to_remove)
- self.assertEqual(expected, method.get("name"))
-
- def test_filter_method_signature_with_L_in_class(self):
- xml = """
- <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
- """
- method = ET.fromstring(xml)
- classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"}
- expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
- api_versions_trimmer.filter_method_tag(method, classes_to_remove)
- self.assertEqual(expected, method.get("name"))
-
- def test_filter_method_signature_with_inner_class(self):
- xml = """
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
- """
- method = ET.fromstring(xml)
- classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"}
- expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
- api_versions_trimmer.filter_method_tag(method, classes_to_remove)
- self.assertEqual(expected, method.get("name"))
-
- def _run_filter_db_test(self, database_str, expected):
- """Performs the pattern of testing the filter_lint_database method.
-
- Filters instances of the class "a/b/C" (hard-coded) from the database string
- and compares the result with the expected result (performs formatting of
- the xml of both inputs)
-
- Args:
- database_str: string, the contents of the lint database (api-versions.xml)
- expected: string, the expected result after filtering the original
- database
- """
- database = io.StringIO(database_str)
- classes_to_remove = {"a/b/C"}
- output = io.BytesIO()
- api_versions_trimmer.filter_lint_database(
- database,
- classes_to_remove,
- output
- )
- expected = pretty_print(expected)
- res = pretty_print(output.getvalue().decode("utf-8"))
- self.assertEqual(expected, res)
-
- def test_filter_lint_database_updates_method_signature_params(self):
- self._run_filter_db_test(
- database_str="""
- <api version="2">
- <!-- will be removed -->
- <class name="a/b/C" since="1">
- <extends name="java/lang/Object"/>
- </class>
-
- <class name="a/b/E" since="1">
- <!-- extends will be modified -->
- <extends name="a/b/C"/>
- <!-- first parameter will be modified -->
- <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/>
- <!-- second should remain untouched -->
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """,
- expected="""
- <api version="2">
- <class name="a/b/E" since="1">
- <extends name="java/lang/Object"/>
- <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """)
-
- def test_filter_lint_database_updates_method_signature_return(self):
- self._run_filter_db_test(
- database_str="""
- <api version="2">
- <!-- will be removed -->
- <class name="a/b/C" since="1">
- <extends name="java/lang/Object"/>
- </class>
-
- <class name="a/b/E" since="1">
- <!-- extends will be modified -->
- <extends name="a/b/C"/>
- <!-- return type should be changed -->
- <method name="gestureIdToString(I)La/b/C;" since="24"/>
- </class>
- </api>
- """,
- expected="""
- <api version="2">
- <class name="a/b/E" since="1">
-
- <extends name="java/lang/Object"/>
-
- <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/>
- </class>
- </api>
- """)
-
- def test_filter_lint_database_removes_implements(self):
- self._run_filter_db_test(
- database_str="""
- <api version="2">
- <!-- will be removed -->
- <class name="a/b/C" since="1">
- <extends name="java/lang/Object"/>
- </class>
-
- <class name="a/b/D" since="1">
- <extends name="java/lang/Object"/>
- <implements name="a/b/C"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """,
- expected="""
- <api version="2">
-
- <class name="a/b/D" since="1">
- <extends name="java/lang/Object"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """)
-
- def test_filter_lint_database_updates_extends(self):
- self._run_filter_db_test(
- database_str="""
- <api version="2">
- <!-- will be removed -->
- <class name="a/b/C" since="1">
- <extends name="java/lang/Object"/>
- </class>
-
- <class name="a/b/E" since="1">
- <!-- extends will be modified -->
- <extends name="a/b/C"/>
- <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """,
- expected="""
- <api version="2">
- <class name="a/b/E" since="1">
- <extends name="java/lang/Object"/>
- <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """)
-
- def test_filter_lint_database_removes_class(self):
- self._run_filter_db_test(
- database_str="""
- <api version="2">
- <!-- will be removed -->
- <class name="a/b/C" since="1">
- <extends name="java/lang/Object"/>
- </class>
-
- <class name="a/b/D" since="1">
- <extends name="java/lang/Object"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """,
- expected="""
- <api version="2">
-
- <class name="a/b/D" since="1">
- <extends name="java/lang/Object"/>
- <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
-sultCallback;Landroid/os/Handler;)Z" since="24"/>
- </class>
- </api>
- """)
-
-
-if __name__ == "__main__":
- unittest.main(verbosity=2)
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 00d9a4b..c4e8b0e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -458,6 +458,7 @@
EGLConfig BootAnimation::getEglConfig(const EGLDisplay& display) {
const EGLint attribs[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 957ebfb..a7560b2 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -40,7 +40,7 @@
public String longHelp() {
return shortHelp() + "\n"
+ "\n"
- + "usage: svc power stayon [true|false|usb|ac|wireless]\n"
+ + "usage: svc power stayon [true|false|usb|ac|wireless|dock]\n"
+ " Set the 'keep awake while plugged in' setting.\n"
+ " svc power reboot [reason]\n"
+ " Perform a runtime shutdown and reboot device with specified reason.\n"
@@ -66,9 +66,10 @@
if ("stayon".equals(args[1]) && args.length == 3) {
int val;
if ("true".equals(args[2])) {
- val = BatteryManager.BATTERY_PLUGGED_AC |
- BatteryManager.BATTERY_PLUGGED_USB |
- BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ val = BatteryManager.BATTERY_PLUGGED_AC
+ | BatteryManager.BATTERY_PLUGGED_USB
+ | BatteryManager.BATTERY_PLUGGED_WIRELESS
+ | BatteryManager.BATTERY_PLUGGED_DOCK;
}
else if ("false".equals(args[2])) {
val = 0;
@@ -78,6 +79,8 @@
val = BatteryManager.BATTERY_PLUGGED_AC;
} else if ("wireless".equals(args[2])) {
val = BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ } else if ("dock".equals(args[2])) {
+ val = BatteryManager.BATTERY_PLUGGED_DOCK;
} else {
break fail;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index b51344b..881a677 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -83,6 +83,7 @@
field public static final String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE";
field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
field public static final String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
+ field public static final String CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS = "android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS";
field public static final String CREDENTIAL_MANAGER_SET_ORIGIN = "android.permission.CREDENTIAL_MANAGER_SET_ORIGIN";
field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
field public static final String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
@@ -13677,8 +13678,9 @@
}
public final class CredentialOption implements android.os.Parcelable {
- ctor public CredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
+ ctor @Deprecated public CredentialOption(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean);
method public int describeContents();
+ method @NonNull public java.util.Set<android.content.ComponentName> getAllowedProviders();
method @NonNull public android.os.Bundle getCandidateQueryData();
method @NonNull public android.os.Bundle getCredentialRetrievalData();
method @NonNull public String getType();
@@ -13688,6 +13690,14 @@
field public static final String FLATTENED_REQUEST = "android.credentials.GetCredentialOption.FLATTENED_REQUEST_STRING";
}
+ public static final class CredentialOption.Builder {
+ ctor public CredentialOption.Builder(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle);
+ method @NonNull public android.credentials.CredentialOption.Builder addAllowedProvider(@NonNull android.content.ComponentName);
+ method @NonNull public android.credentials.CredentialOption build();
+ method @NonNull public android.credentials.CredentialOption.Builder setAllowedProviders(@NonNull java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.credentials.CredentialOption.Builder setIsSystemProviderRequired(boolean);
+ }
+
public class GetCredentialException extends java.lang.Exception {
ctor public GetCredentialException(@NonNull String, @Nullable String);
ctor public GetCredentialException(@NonNull String, @Nullable String, @Nullable Throwable);
@@ -37091,6 +37101,7 @@
field public static final String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
field public static final String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
field public static final String ACTION_CONDITION_PROVIDER_SETTINGS = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS";
+ field public static final String ACTION_CREDENTIAL_PROVIDER = "android.settings.CREDENTIAL_PROVIDER";
field public static final String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
field public static final String ACTION_DATA_USAGE_SETTINGS = "android.settings.DATA_USAGE_SETTINGS";
field public static final String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index b96f8c9..0293bb5 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -474,6 +474,12 @@
public abstract BackgroundStartPrivileges getBackgroundStartPrivileges(int uid);
public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
+ /**
+ * Returns whether the app is in a state where it is allowed to schedule a
+ * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
+ */
+ public abstract boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName);
+
/** @see com.android.server.am.ActivityManagerService#monitor */
public abstract void monitor();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 682fec8..4306d78 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -119,6 +119,8 @@
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.DdmSyncStageUpdater;
+import android.os.DdmSyncState.Stage;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
@@ -264,6 +266,9 @@
*/
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
+
+ private final DdmSyncStageUpdater mDdmSyncStageUpdater = new DdmSyncStageUpdater();
+
/** @hide */
public static final String TAG = "ActivityThread";
private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
@@ -6689,6 +6694,8 @@
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
+ mDdmSyncStageUpdater.next(Stage.Bind);
+
// Register the UI Thread as a sensitive thread to the runtime.
VMRuntime.registerSensitiveThread();
// In the case the stack depth property exists, pass it down to the runtime.
@@ -6738,6 +6745,7 @@
data.appInfo.packageName,
UserHandle.myUserId());
VMRuntime.setProcessPackageName(data.appInfo.packageName);
+ mDdmSyncStageUpdater.next(Stage.Named);
// Pass data directory path to ART. This is used for caching information and
// should be set before any application code is loaded.
@@ -6942,6 +6950,7 @@
final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
+ mDdmSyncStageUpdater.next(Stage.Debugger);
if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
waitForDebugger(data);
} else if (data.debugMode == ApplicationThreadConstants.DEBUG_SUSPEND) {
@@ -6949,6 +6958,7 @@
}
// Nothing special to do in case of DEBUG_ON.
}
+ mDdmSyncStageUpdater.next(Stage.Running);
try {
// If the app is being launched for full backup or restore, bring it up in
@@ -7852,6 +7862,7 @@
mConfigurationController = new ConfigurationController(this);
mSystemThread = system;
mStartSeq = startSeq;
+ mDdmSyncStageUpdater.next(Stage.Attach);
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 0bdc222..1df8602 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -42,10 +42,10 @@
},
{
"file_patterns": ["(/|^)AppOpsManager.java"],
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ "include-filter": "android.permissionpolicy.cts.RuntimePermissionProperties"
}
]
},
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 303ada0..404f94a 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -188,6 +188,14 @@
public int launchIntoPipHostTaskId;
/**
+ * The task id of the parent Task of the launch-into-pip Activity, i.e., if task have more than
+ * one activity it will create new task for this activity, this id is the origin task id and
+ * the pip activity will be reparent to origin task when it exit pip mode.
+ * @hide
+ */
+ public int lastParentTaskIdBeforePip;
+
+ /**
* The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of
* (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS),
* {@code null} otherwise.
@@ -512,6 +520,7 @@
pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
shouldDockBigOverlays = source.readBoolean();
launchIntoPipHostTaskId = source.readInt();
+ lastParentTaskIdBeforePip = source.readInt();
displayCutoutInsets = source.readTypedObject(Rect.CREATOR);
topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
isResizeable = source.readBoolean();
@@ -558,6 +567,7 @@
dest.writeTypedObject(pictureInPictureParams, flags);
dest.writeBoolean(shouldDockBigOverlays);
dest.writeInt(launchIntoPipHostTaskId);
+ dest.writeInt(lastParentTaskIdBeforePip);
dest.writeTypedObject(displayCutoutInsets, flags);
dest.writeTypedObject(topActivityInfo, flags);
dest.writeBoolean(isResizeable);
@@ -598,6 +608,7 @@
+ " pictureInPictureParams=" + pictureInPictureParams
+ " shouldDockBigOverlays=" + shouldDockBigOverlays
+ " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId
+ + " lastParentTaskIdBeforePip=" + lastParentTaskIdBeforePip
+ " displayCutoutSafeInsets=" + displayCutoutInsets
+ " topActivityInfo=" + topActivityInfo
+ " launchCookies=" + launchCookies
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index aa302ad..2674985 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -309,14 +309,30 @@
try {
if (checkGetTypePermission(attributionSource, uri)
== PermissionChecker.PERMISSION_GRANTED) {
- final String type = mInterface.getType(uri);
+ String type;
+ if (checkPermission(Manifest.permission.GET_ANY_PROVIDER_TYPE,
+ attributionSource) == PermissionChecker.PERMISSION_GRANTED) {
+ /*
+ For calling packages having the special permission for any type,
+ the calling identity should be cleared before calling getType.
+ */
+ final CallingIdentity origId = getContentProvider().clearCallingIdentity();
+ try {
+ type = mInterface.getType(uri);
+ } finally {
+ getContentProvider().restoreCallingIdentity(origId);
+ }
+ } else {
+ type = mInterface.getType(uri);
+ }
+
if (type != null) {
logGetTypeData(Binder.getCallingUid(), uri, type, true);
}
return type;
} else {
final int callingUid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
+ final CallingIdentity origId = getContentProvider().clearCallingIdentity();
try {
final String type = getTypeAnonymous(uri);
if (type != null) {
@@ -324,7 +340,7 @@
}
return type;
} finally {
- Binder.restoreCallingIdentity(origId);
+ getContentProvider().restoreCallingIdentity(origId);
}
}
} catch (RemoteException e) {
diff --git a/core/java/android/credentials/CredentialOption.java b/core/java/android/credentials/CredentialOption.java
index 9a3b46d..da6656a 100644
--- a/core/java/android/credentials/CredentialOption.java
+++ b/core/java/android/credentials/CredentialOption.java
@@ -16,16 +16,25 @@
package android.credentials;
+import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;
+
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.ComponentName;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArraySet;
+
+import androidx.annotation.RequiresPermission;
import com.android.internal.util.AnnotationValidations;
import com.android.internal.util.Preconditions;
+import java.util.Set;
+
/**
* Information about a specific type of credential to be requested during a {@link
* CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor,
@@ -66,6 +75,14 @@
private final boolean mIsSystemProviderRequired;
/**
+ * A list of {@link ComponentName}s corresponding to the providers that this option must be
+ * queried against.
+ */
+ @NonNull
+ private final ArraySet<ComponentName> mAllowedProviders;
+
+
+ /**
* Returns the requested credential type.
*/
@NonNull
@@ -105,12 +122,22 @@
return mIsSystemProviderRequired;
}
+ /**
+ * Returns the set of {@link ComponentName} corresponding to providers that must receive
+ * this option.
+ */
+ @NonNull
+ public Set<ComponentName> getAllowedProviders() {
+ return mAllowedProviders;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mType);
dest.writeBundle(mCredentialRetrievalData);
dest.writeBundle(mCandidateQueryData);
dest.writeBoolean(mIsSystemProviderRequired);
+ dest.writeArraySet(mAllowedProviders);
}
@Override
@@ -125,6 +152,7 @@
+ ", requestData=" + mCredentialRetrievalData
+ ", candidateQueryData=" + mCandidateQueryData
+ ", isSystemProviderRequired=" + mIsSystemProviderRequired
+ + ", allowedProviders=" + mAllowedProviders
+ "}";
}
@@ -139,17 +167,50 @@
* provider
* @throws IllegalArgumentException If type is empty.
*/
- public CredentialOption(
+ private CredentialOption(
@NonNull String type,
@NonNull Bundle credentialRetrievalData,
@NonNull Bundle candidateQueryData,
- boolean isSystemProviderRequired) {
+ boolean isSystemProviderRequired,
+ @NonNull ArraySet<ComponentName> allowedProviders) {
mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
"requestData must not be null");
mCandidateQueryData = requireNonNull(candidateQueryData,
"candidateQueryData must not be null");
mIsSystemProviderRequired = isSystemProviderRequired;
+ mAllowedProviders = requireNonNull(allowedProviders, "providerFilterSer must"
+ + "not be empty");
+ }
+
+ /**
+ * Constructs a {@link CredentialOption}.
+ *
+ * @param type the requested credential type
+ * @param credentialRetrievalData the request data
+ * @param candidateQueryData the partial request data that will be sent to the provider
+ * during the initial credential candidate query stage
+ * @param isSystemProviderRequired whether the request must only be fulfilled by a system
+ * provider
+ * @throws IllegalArgumentException If type is empty, or null.
+ * @throws NullPointerException If {@code credentialRetrievalData}, or
+ * {@code candidateQueryData} is null.
+ *
+ * @deprecated replaced by Builder
+ */
+ @Deprecated
+ public CredentialOption(
+ @NonNull String type,
+ @NonNull Bundle credentialRetrievalData,
+ @NonNull Bundle candidateQueryData,
+ boolean isSystemProviderRequired) {
+ this(
+ type,
+ credentialRetrievalData,
+ candidateQueryData,
+ isSystemProviderRequired,
+ new ArraySet<>()
+ );
}
private CredentialOption(@NonNull Parcel in) {
@@ -165,6 +226,8 @@
mCandidateQueryData = candidateQueryData;
AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData);
mIsSystemProviderRequired = isSystemProviderRequired;
+ mAllowedProviders = (ArraySet<ComponentName>) in.readArraySet(null);
+ AnnotationValidations.validate(NonNull.class, null, mAllowedProviders);
}
@NonNull
@@ -179,4 +242,108 @@
return new CredentialOption(in);
}
};
+
+ /** A builder for {@link CredentialOption}. */
+ public static final class Builder {
+
+ @NonNull
+ private String mType;
+
+ @NonNull
+ private Bundle mCredentialRetrievalData;
+
+ @NonNull
+ private Bundle mCandidateQueryData;
+
+ private boolean mIsSystemProviderRequired = false;
+
+ @NonNull
+ private ArraySet<ComponentName> mAllowedProviders = new ArraySet<>();
+
+ /**
+ * @param type the type of the credential option
+ * @param credentialRetrievalData the full request data
+ * @param candidateQueryData the partial request data that will be sent to the provider
+ * during the initial credential candidate query stage.
+ * @throws IllegalArgumentException If {@code type} is null, or empty
+ * @throws NullPointerException If {@code credentialRetrievalData}, or
+ * {@code candidateQueryData} is null
+ */
+ public Builder(@NonNull String type, @NonNull Bundle credentialRetrievalData,
+ @NonNull Bundle candidateQueryData) {
+ mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ + "null, or empty");
+ mCredentialRetrievalData = requireNonNull(credentialRetrievalData,
+ "credentialRetrievalData must not be null");
+ mCandidateQueryData = requireNonNull(candidateQueryData,
+ "candidateQueryData must not be null");
+ }
+
+ /**
+ * Sets a true/false value corresponding to whether this option must be serviced by
+ * system credentials providers only.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setIsSystemProviderRequired(boolean isSystemProviderRequired) {
+ mIsSystemProviderRequired = isSystemProviderRequired;
+ return this;
+ }
+
+ /**
+ * Adds a provider {@link ComponentName} to be queried while gathering credentials from
+ * credential providers on the device.
+ *
+ * If no candidate providers are specified, all user configured and system credential
+ * providers will be queried in the candidate query phase.
+ *
+ * If an invalid component name is provided, or a service corresponding to the
+ * component name does not exist on the device, that component name is ignored.
+ * If all component names are invalid, or not present on the device, no providers
+ * are queried and no credentials are retrieved.
+ *
+ * @throws NullPointerException If {@code allowedProvider} is null
+ */
+ @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)
+ @NonNull
+ public Builder addAllowedProvider(@NonNull ComponentName allowedProvider) {
+ mAllowedProviders.add(requireNonNull(allowedProvider,
+ "allowedProvider must not be null"));
+ return this;
+ }
+
+ /**
+ * Sets a set of provider {@link ComponentName} to be queried while gathering credentials
+ * from credential providers on the device.
+ *
+ * If no candidate providers are specified, all user configured and system credential
+ * providers will be queried in the candidate query phase.
+ *
+ * If an invalid component name is provided, or a service corresponding to the
+ * component name does not exist on the device, that component name is ignored.
+ * If all component names are invalid, or not present on the device, no providers
+ * are queried and no credentials are retrieved.
+ *
+ * @throws NullPointerException If {@code allowedProviders} is null, or any of its
+ * elements are null.
+ */
+ @RequiresPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)
+ @NonNull
+ public Builder setAllowedProviders(@NonNull Set<ComponentName> allowedProviders) {
+ Preconditions.checkCollectionElementsNotNull(
+ allowedProviders,
+ /*valueName=*/ "allowedProviders");
+ mAllowedProviders = new ArraySet<>(allowedProviders);
+ return this;
+ }
+
+ /**
+ * Builds a {@link CredentialOption}.
+ */
+ @NonNull
+ public CredentialOption build() {
+ return new CredentialOption(mType, mCredentialRetrievalData, mCandidateQueryData,
+ mIsSystemProviderRequired, mAllowedProviders);
+ }
+ }
}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 4160029..a51a740 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -16,6 +16,7 @@
package android.ddm;
+import android.os.DdmSyncState;
import android.os.Debug;
import android.os.UserHandle;
import android.util.Log;
@@ -44,6 +45,7 @@
private static final String[] FRAMEWORK_FEATURES = new String[] {
"opengl-tracing",
"view-hierarchy",
+ "support_boot_stages"
};
/* singleton, do not instantiate */
@@ -145,7 +147,9 @@
+ instructionSetDescription.length() * 2
+ vmFlags.length() * 2
+ 1
- + pkgName.length() * 2);
+ + pkgName.length() * 2
+ // STAG id (int)
+ + Integer.BYTES);
out.order(ChunkHandler.CHUNK_ORDER);
out.putInt(CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
@@ -162,6 +166,10 @@
out.putInt(pkgName.length());
putString(out, pkgName);
+ // Added API 34 (and advertised via FEAT ddm packet)
+ // Send the current boot stage in ActivityThread
+ out.putInt(DdmSyncState.getStage().toInt());
+
Chunk reply = new Chunk(CHUNK_HELO, out);
/*
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index e9df553..1bc6099 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -661,7 +661,7 @@
*
* <p>Repeating burst requests are a simple way for an application to
* maintain a preview or other continuous stream of frames where each
- * request is different in a predicatable way, without having to continually
+ * request is different in a predictable way, without having to continually
* submit requests through {@link #captureBurst}.</p>
*
* <p>To stop the repeating capture, call {@link #stopRepeating}. Any
@@ -902,7 +902,7 @@
* {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}
* capability in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}. When this method
* is supported, applications can use it to improve the latency of closing camera or recreating
- * capture session without losing the in progresss capture request outputs.</p>
+ * capture session without losing the in progress capture request outputs.</p>
*
* <p>Offline processing mode and the corresponding {@link CameraOfflineSession} differ from
* a regular online camera capture session in several ways. Successful offline switches will
@@ -1001,7 +1001,7 @@
*
* <p>Note that for common usage scenarios like creating a new session or closing the camera
* device, it is faster to call respective APIs directly (see below for more details) without
- * calling into this method. This API is only useful when application wants to uncofigure the
+ * calling into this method. This API is only useful when application wants to unconfigure the
* camera but keep the device open for later use.</p>
*
* <p>Creating a new capture session with {@link CameraDevice#createCaptureSession}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index dfb9cf6..0e4c3c0 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -651,7 +651,7 @@
* @param metadataClass The subclass of CameraMetadata that you want to get the keys for.
* @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class
* @param filterTags An array of tags to be used for filtering
- * @param includeSynthetic Include public syntethic tag by default.
+ * @param includeSynthetic Include public synthetic tag by default.
*
* @return List of keys supported by this CameraDevice for metadataClass.
*
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 42aa608..5feda78 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -141,7 +141,7 @@
* parameters. All automatic control is disabled (auto-exposure, auto-white
* balance, auto-focus), and post-processing parameters are set to preview
* quality. The manual capture parameters (exposure, sensitivity, and so on)
- * are set to reasonable defaults, but should be overriden by the
+ * are set to reasonable defaults, but should be overridden by the
* application depending on the intended use case.
* This template is guaranteed to be supported on camera devices that support the
* {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}
@@ -680,7 +680,7 @@
* <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
- * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processsing.</td> </tr>
+ * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processing.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>Video recording with maximum-size video snapshot</td> </tr>
* <tr> <td>{@code YUV }</td><td id="rb">{@code 640x480}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard video recording plus maximum-resolution in-app processing.</td> </tr>
* <tr> <td>{@code YUV }</td><td id="rb">{@code 640x480}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Preview plus two-input maximum-resolution in-app processing.</td> </tr>
@@ -722,7 +722,7 @@
* <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
- * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input in-app processsing.</td> </tr>
+ * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input in-app processing.</td> </tr>
* </table><br>
* </p>
*
@@ -1137,7 +1137,7 @@
* <tr><th colspan="13">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors (YUV / PRIV inputs are guaranteed only if YUV / PRIVATE reprocessing are supported)</th></tr>
* <tr> <th colspan="3" id="rb">Input</th> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th> <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
* <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th><th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr>
- * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>RAW remosaic reprocessing with seperate preview</td> </tr>
+ * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>RAW remosaic reprocessing with separate preview</td> </tr>
* <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>Ultra high res RAW -> JPEG / YUV with seperate preview</td> </tr>
* <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code YUV / PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG }</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td> Ultra high res PRIV / YUV -> YUV / JPEG reprocessing with seperate preview</td> </tr>
* </table><br>
@@ -1260,7 +1260,7 @@
* settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p>
*
* <p>Individual physical camera settings will only be honored for camera session
- * that was initialiazed with corresponding physical camera id output configuration
+ * that was initialized with corresponding physical camera id output configuration
* {@link OutputConfiguration#setPhysicalCameraId} and the same output targets are
* also attached in the request by {@link CaptureRequest.Builder#addTarget}.</p>
*
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 696873f..144b1de 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -256,7 +256,7 @@
/**
* Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with
- * cameraserver in order to get the list of camera ids. This is to faciliate testing since some
+ * cameraserver in order to get the list of camera ids. This is to facilitate testing since some
* camera ids may go 'offline' without callbacks from cameraserver because of changes in
* SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call
* adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call
@@ -560,7 +560,7 @@
}
// Query the characteristics of all physical sub-cameras, and combine the multi-resolution
- // stream configurations. Alternatively, for ultra-high resolution camera, direclty use
+ // stream configurations. Alternatively, for ultra-high resolution camera, directly use
// its multi-resolution stream configurations. Note that framework derived formats such as
// HEIC and DEPTH_JPEG aren't supported as multi-resolution input or output formats.
Set<String> physicalCameraIds = info.getPhysicalCameraIds();
@@ -835,7 +835,7 @@
* Opening the same camera ID twice in the same application will similarly cause the
* {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback
* being fired for the {@link CameraDevice} from the first open call and all ongoing tasks
- * being droppped.</p>
+ * being dropped.</p>
*
* <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
* be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
@@ -1759,7 +1759,7 @@
private final Set<Set<String>> mConcurrentCameraIdCombinations =
new ArraySet<Set<String>>();
- // Registered availablility callbacks and their executors
+ // Registered availability callbacks and their executors
private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
new ArrayMap<AvailabilityCallback, Executor>();
@@ -2846,7 +2846,7 @@
// Tell listeners that the cameras and torch modes are unavailable and schedule a
// reconnection to camera service. When camera service is reconnected, the camera
// and torch statuses will be updated.
- // Iterate from the end to the beginning befcause onStatusChangedLocked removes
+ // Iterate from the end to the beginning because onStatusChangedLocked removes
// entries from the ArrayMap.
for (int i = mDeviceStatus.size() - 1; i >= 0; i--) {
String cameraId = mDeviceStatus.keyAt(i);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ea99847..a7e28e2 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -159,7 +159,7 @@
* Optionally, if {@code filterTags} is not {@code null}, then filter out any keys
* whose native {@code tag} is not in {@code filterTags}. The {@code filterTags} array will be
* sorted as a side effect.
- * {@code includeSynthetic} Includes public syntenthic fields by default.
+ * {@code includeSynthetic} Includes public synthetic fields by default.
* </p>
*/
/*package*/ @SuppressWarnings("unchecked")
@@ -2308,7 +2308,7 @@
/**
* <p>An external flash has been turned on.</p>
* <p>It informs the camera device that an external flash has been turned on, and that
- * metering (and continuous focus if active) should be quickly recaculated to account
+ * metering (and continuous focus if active) should be quickly recalculated to account
* for the external flash. Otherwise, this mode acts like ON.</p>
* <p>When the external flash is turned off, AE mode should be changed to one of the
* other available AE modes.</p>
diff --git a/core/java/android/hardware/camera2/CameraOfflineSession.java b/core/java/android/hardware/camera2/CameraOfflineSession.java
index 312559c..c219886 100644
--- a/core/java/android/hardware/camera2/CameraOfflineSession.java
+++ b/core/java/android/hardware/camera2/CameraOfflineSession.java
@@ -152,7 +152,7 @@
*
* <p>Closing a session is idempotent; closing more than once has no effect.</p>
*
- * @throws IllegalStateException if the offline sesion is not ready.
+ * @throws IllegalStateException if the offline session is not ready.
*/
@Override
public abstract void close();
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index cc484ea..1ae2fe1 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -482,7 +482,7 @@
}
private static final int DEFAULT_PIXEL_STRIDE = 2; // bytes per sample
- private static final int BYTES_PER_RGB_PIX = 3; // byts per pixel
+ private static final int BYTES_PER_RGB_PIX = 3; // bytes per pixel
// TIFF tag values needed to map between public API and TIFF spec
private static final int TAG_ORIENTATION_UNKNOWN = 9;
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
index 34d016a..7c54a9b 100644
--- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
+++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
@@ -36,4 +36,5 @@
int surfaceGroupId;
String physicalCameraId;
List<CameraOutputConfig> sharedSurfaceConfigs;
+ boolean isMultiResolutionOutput;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 2fa8b87..cfade55 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -258,6 +258,9 @@
OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId,
outputSurface);
+ if (output.isMultiResolutionOutput) {
+ cameraOutput.setMultiResolutionOutput();
+ }
if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) {
cameraOutput.enableSurfaceSharing();
for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) {
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 86c453b..0a4a1f0 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -607,7 +607,7 @@
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM) },
- "Maximum-resolution two-input in-app processsing"),
+ "Maximum-resolution two-input in-app processing"),
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW),
@@ -891,7 +891,7 @@
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
- "Standard stil image capture"),
+ "Standard still image capture"),
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 857f62d..21540bf 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -165,7 +165,7 @@
* device runs in fixed frame rate. The timestamp is roughly in the same time base as
* {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For an output surface of MediaRecorder, MediaCodec, or ImageReader with {@link
- * android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usge flag, the timestamp base is
+ * android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usage flag, the timestamp base is
* {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
* {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For all other cases, the timestamp base is {@link #TIMESTAMP_BASE_SENSOR}, the same
@@ -418,7 +418,7 @@
* call, or no non-negative group ID has been set.
* @hide
*/
- void setMultiResolutionOutput() {
+ public void setMultiResolutionOutput() {
if (mIsShared) {
throw new IllegalStateException("Multi-resolution output flag must not be set for " +
"configuration with surface sharing");
@@ -654,7 +654,7 @@
mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
} else {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
- throw new IllegalArgumentException("Unknow surface source class type");
+ throw new IllegalArgumentException("Unknown surface source class type");
}
if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
@@ -715,7 +715,7 @@
* The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
* MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
*
- * <p>This function must not be called from OuptutConfigurations created by {@link
+ * <p>This function must not be called from OutputConfigurations created by {@link
* #createInstancesForMultiResolutionOutput}.</p>
*
* @throws IllegalStateException If this OutputConfiguration is created via {@link
@@ -934,7 +934,7 @@
*
* <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
* OutputConfiguration. The only notable exception is the surface associated with
- * the OutputConfigration see {@link #getSurface} which was passed as part of the constructor
+ * the OutputConfiguration see {@link #getSurface} which was passed as part of the constructor
* or was added first in the deferred case
* {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
*
@@ -962,7 +962,7 @@
* for scenarios where the immediate consumer target isn't sufficient to indicate the stream's
* usage.</p>
*
- * <p>The main difference beteween stream use case and capture intent is that the former
+ * <p>The main difference between stream use case and capture intent is that the former
* enables the camera device to optimize camera hardware and software pipelines based on user
* scenarios for each stream, whereas the latter is mainly a hint to camera to decide
* optimal 3A strategy that's applicable to the whole session. The camera device carries out
@@ -1123,7 +1123,7 @@
* CameraCharacteristics#SENSOR_READOUT_TIMESTAMP} is
* {@link CameraMetadata#SENSOR_READOUT_TIMESTAMP_HARDWARE}.</p>
*
- * <p>As long as readout timestamp is supported, if the timestamp base isi
+ * <p>As long as readout timestamp is supported, if the timestamp base is
* {@link #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}, or if the timestamp base is DEFAULT for a
* SurfaceView output, the image timestamps for the output are always readout time regardless
* of whether this function is called.</p>
@@ -1420,9 +1420,9 @@
*/
@Override
public int hashCode() {
- // Need ensure that the hashcode remains unchanged after adding a deferred surface. Otherwise
- // the deferred output configuration will be lost in the camera streammap after the deferred
- // surface is set.
+ // Need ensure that the hashcode remains unchanged after adding a deferred surface.
+ // Otherwise the deferred output configuration will be lost in the camera stream map
+ // after the deferred surface is set.
if (mIsDeferredConfig) {
return HashCodeHelpers.hashCode(
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
@@ -1446,7 +1446,7 @@
private static final String TAG = "OutputConfiguration";
// A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
- // incremented everytime {@link createInstancesForMultiResolutionOutput} is called.
+ // incremented every time {@link createInstancesForMultiResolutionOutput} is called.
private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
private ArrayList<Surface> mSurfaces;
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 5a48176..aabe149 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1790,7 +1790,7 @@
*
* <p>{@code ValidOutputFormatsForInput([in:%s(%d), out:%s(%d), ... %s(%d)],
* ... [in:%s(%d), out:%s(%d), ... %s(%d)])}, where {@code [in:%s(%d), out:%s(%d), ... %s(%d)]}
- * represents an input fomat and its valid output formats.</p>
+ * represents an input format and its valid output formats.</p>
*
* <p>{@code HighSpeedVideoConfigurations([w:%d, h:%d, min_fps:%d, max_fps:%d],
* ... [w:%d, h:%d, min_fps:%d, max_fps:%d])}, where
diff --git a/core/java/android/hardware/display/HdrConversionMode.java b/core/java/android/hardware/display/HdrConversionMode.java
index 49e5eff..5fccb5e 100644
--- a/core/java/android/hardware/display/HdrConversionMode.java
+++ b/core/java/android/hardware/display/HdrConversionMode.java
@@ -29,9 +29,6 @@
/**
* Describes the HDR conversion mode for a device.
*
- * This class is used when user changes the HDR conversion mode of the device via
- * {@link DisplayManager#setHdrConversionMode(HdrConversionMode)}.
- * <p>
* HDR conversion mode has a conversionMode and preferredHdrOutputType. </p><p>
* The conversionMode can be one of:
* {@link HdrConversionMode#HDR_CONVERSION_UNSUPPORTED} : HDR conversion is unsupported. In this
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 490e55b..03d6d91 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -161,9 +161,8 @@
}
/**
- * Returns the recording session associated with this VirtualDisplay. Only used for
+ * Returns the recording session associated with this {@link VirtualDisplay}. Only used for
* recording via {@link MediaProjection}.
- *
* @hide
*/
@Nullable
@@ -438,7 +437,7 @@
*
* <p>For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
* a 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded up
- * down to a divisor of the physical display. If unset or zero, the virtual display will be
+ * to a divisor of the physical display. If unset or zero, the virtual display will be
* refreshed at the physical display refresh rate.
*
* @see Display#getRefreshRate()
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 089b159..0ba0c5a 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -88,6 +88,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param timeout timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void setTimeout(int timeout) {
try {
@@ -106,6 +107,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @return timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public int getTimeout() {
try {
@@ -167,6 +169,7 @@
* @return response bytes received, will not be null
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or this operation is canceled
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public byte[] transceive(byte[] data) throws IOException {
return transceive(data, true);
@@ -193,6 +196,7 @@
* support.
*
* @return whether the NFC adapter on this device supports extended length APDUs.
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public boolean isExtendedLengthApduSupported() {
try {
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 080e058..26f54e6 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -597,6 +597,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param timeout timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void setTimeout(int timeout) {
try {
@@ -615,6 +616,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @return timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public int getTimeout() {
try {
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index dec2c65..c0416a3 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -236,6 +236,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param timeout timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void setTimeout(int timeout) {
try {
@@ -255,6 +256,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @return timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public int getTimeout() {
try {
diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java
index 39c355a..7d83f15 100644
--- a/core/java/android/nfc/tech/Ndef.java
+++ b/core/java/android/nfc/tech/Ndef.java
@@ -261,6 +261,7 @@
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or the operation is canceled
* @throws FormatException if the NDEF Message on the tag is malformed
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public NdefMessage getNdefMessage() throws IOException, FormatException {
checkConnected();
@@ -301,6 +302,7 @@
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or the operation is canceled
* @throws FormatException if the NDEF Message to write is malformed
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
checkConnected();
@@ -339,6 +341,7 @@
* <p>Does not cause any RF activity and does not block.
*
* @return true if it is possible to make this tag read-only
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public boolean canMakeReadOnly() {
INfcTag tagService = mTag.getTagService();
@@ -370,6 +373,7 @@
* @return true on success, false if it is not possible to make this tag read-only
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or the operation is canceled
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public boolean makeReadOnly() throws IOException {
checkConnected();
diff --git a/core/java/android/nfc/tech/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java
index 4175cd0..f19d302 100644
--- a/core/java/android/nfc/tech/NdefFormatable.java
+++ b/core/java/android/nfc/tech/NdefFormatable.java
@@ -111,6 +111,7 @@
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or the operation is canceled
* @throws FormatException if the NDEF Message to write is malformed
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException {
format(firstMessage, true);
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
index 88730f9..7e66483 100644
--- a/core/java/android/nfc/tech/NfcA.java
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -141,6 +141,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param timeout timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void setTimeout(int timeout) {
try {
@@ -159,6 +160,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @return timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public int getTimeout() {
try {
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index 4487121..2ccd388 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -145,6 +145,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param timeout timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void setTimeout(int timeout) {
try {
@@ -163,6 +164,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @return timeout value in milliseconds
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public int getTimeout() {
try {
diff --git a/core/java/android/nfc/tech/TagTechnology.java b/core/java/android/nfc/tech/TagTechnology.java
index 0e2c7c1..839fe42 100644
--- a/core/java/android/nfc/tech/TagTechnology.java
+++ b/core/java/android/nfc/tech/TagTechnology.java
@@ -176,6 +176,7 @@
* @see #close()
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or connect is canceled
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void connect() throws IOException;
@@ -193,6 +194,7 @@
* @see #close()
* @throws TagLostException if the tag leaves the field
* @throws IOException if there is an I/O failure, or connect is canceled
+ * @throws SecurityException if the tag object is reused after the tag has left the field
* @hide
*/
public void reconnect() throws IOException;
@@ -205,6 +207,7 @@
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @see #connect()
+ * @throws SecurityException if the tag object is reused after the tag has left the field
*/
public void close() throws IOException;
diff --git a/core/java/android/os/DdmSyncStageUpdater.java b/core/java/android/os/DdmSyncStageUpdater.java
new file mode 100644
index 0000000..90f7076
--- /dev/null
+++ b/core/java/android/os/DdmSyncStageUpdater.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.DdmSyncState.Stage;
+import android.util.Slog;
+
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+// Making it public so ActivityThread can access it.
+public class DdmSyncStageUpdater {
+
+ private static final String TAG = "DdmSyncStageUpdater";
+
+ private static final int CHUNK_STAGE = ChunkHandler.type("STAG");
+
+ /**
+ * @hide
+ */
+ public DdmSyncStageUpdater() {
+ }
+
+ /**
+ * @hide
+ */
+ // Making it public so ActivityThread can access it.
+ public synchronized void next(Stage stage) {
+ try {
+ DdmSyncState.next(stage);
+
+ // Request DDMServer to send a STAG chunk
+ ByteBuffer data = ByteBuffer.allocate(Integer.BYTES);
+ data.putInt(stage.toInt());
+ Chunk stagChunk = new Chunk(CHUNK_STAGE, data);
+ DdmServer.sendChunk(stagChunk);
+ } catch (Exception e) {
+ // Catch everything to make sure we don't impact ActivityThread
+ Slog.w(TAG, "Unable to go to next stage" + stage, e);
+ }
+ }
+
+}
diff --git a/core/java/android/os/DdmSyncState.java b/core/java/android/os/DdmSyncState.java
new file mode 100644
index 0000000..09c9ef2
--- /dev/null
+++ b/core/java/android/os/DdmSyncState.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.util.Arrays;
+
+/**
+ * Keep track of an app boot state. The main purpose is to stream back DDM packet so a DDM client
+ * can synchronize with the app state.
+ *
+ * The state is static so it can be accessed from HELO handler.
+ *
+ * @hide
+ **/
+public final class DdmSyncState {
+
+ /**
+ * @hide
+ */
+ public enum Stage {
+ // From zygote to attach
+ Boot("BOOT"),
+
+ // From attach to handleBindApplication
+ Attach("ATCH"),
+
+ // When handleBindApplication is finally reached
+ Bind("BIND"),
+
+ // When the actual package name is known (not the early "<preinitalized>" value).
+ Named("NAMD"),
+
+ // Can be skipped if the app is not debugged.
+ Debugger("DEBG"),
+
+ // App is in RunLoop
+ Running("A_GO");
+
+ final String mLabel;
+
+ Stage(String label) {
+ if (label.length() != 4) {
+ throw new IllegalStateException(
+ "Bad stage id '" + label + "'. Must be four letters");
+ }
+ this.mLabel = label;
+ }
+
+ /**
+ * To be included in a DDM packet payload, the stage is encoded in a big-endian int
+ * @hide
+ */
+ public int toInt() {
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result = ((result << 8) | (mLabel.charAt(i) & 0xff));
+ }
+ return result;
+ }
+ }
+
+ private static int sCurrentStageIndex = 0;
+
+ /**
+ * @hide
+ */
+ public static synchronized Stage getStage() {
+ return Stage.values()[sCurrentStageIndex];
+ }
+
+ /**
+ * @hide
+ */
+ public static void reset() {
+ sCurrentStageIndex = 0;
+ }
+
+ /**
+ * Search for the next level down the list of Stage. Only succeed if the next stage
+ * if a later stage (no cycling allowed).
+ *
+ * @hide
+ */
+ public static synchronized void next(Stage nextStage) {
+ Stage[] stages = Stage.values();
+ // Search for the requested next stage
+ int rover = sCurrentStageIndex;
+ while (rover < stages.length && stages[rover] != nextStage) {
+ rover++;
+ }
+
+ if (rover == stages.length || stages[rover] != nextStage) {
+ throw new IllegalStateException(
+ "Cannot go to " + nextStage + " from:" + getInternalState());
+ }
+
+ sCurrentStageIndex = rover;
+ }
+
+ /**
+ * Use to build error messages
+ * @hide
+ */
+ private static String getInternalState() {
+ StringBuilder sb = new StringBuilder("\n");
+ sb.append("level = ").append(sCurrentStageIndex);
+ sb.append("\n");
+ sb.append("stages = ");
+ sb.append(Arrays.toString(Arrays.stream(Stage.values()).map(Enum::name).toArray()));
+ sb.append("\n");
+ return sb.toString();
+ }
+}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index e9a3254..69889c5 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -79,3 +79,7 @@
# ART
per-file ArtModuleServiceManager.java = file:platform/art:/OWNERS
+
+# DDM Protocol
+per-file DdmSyncState.java = sanglardf@google.com, rpaquay@google.com
+per-file DdmSyncStageUpdater.java = sanglardf@google.com, rpaquay@google.com
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1673ade..e784c26 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4499,17 +4499,28 @@
public void writeToParcel(Parcel out) {
Parcel source = mSource;
if (source != null) {
- out.appendFrom(source, mPosition, mLength);
- } else {
- out.writeValue(mObject);
+ synchronized (source) {
+ if (mSource != null) {
+ out.appendFrom(source, mPosition, mLength);
+ return;
+ }
+ }
}
+
+ out.writeValue(mObject);
}
public boolean hasFileDescriptors() {
Parcel source = mSource;
- return (source != null)
- ? source.hasFileDescriptors(mPosition, mLength)
- : Parcel.hasFileDescriptors(mObject);
+ if (source != null) {
+ synchronized (source) {
+ if (mSource != null) {
+ return source.hasFileDescriptors(mPosition, mLength);
+ }
+ }
+ }
+
+ return Parcel.hasFileDescriptors(mObject);
}
@Override
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d62862c..326f87e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2428,6 +2428,24 @@
"android.settings.REQUEST_SET_AUTOFILL_SERVICE";
/**
+ * Activity Action: Show screen that let user enable a Credential Manager provider.
+ * <p>
+ * Input: Intent's data URI set with an application name, using the
+ * "package" schema (like "package:com.my.app").
+ *
+ * <p>
+ * Output: {@link android.app.Activity#RESULT_OK} if user selected a provider belonging
+ * to the caller package.
+ * <p>
+ * <b>NOTE: </b> Applications should call
+ * {@link android.credentials.CredentialManager#isEnabledCredentialProviderService()}
+ * and only use this action to start an activity if they return {@code false}.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CREDENTIAL_PROVIDER =
+ "android.settings.CREDENTIAL_PROVIDER";
+
+ /**
* Activity Action: Show screen for controlling the Quick Access Wallet.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -14788,23 +14806,6 @@
"adaptive_battery_management_enabled";
/**
- * Whether or not apps are allowed into the
- * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
- * Type: int (0 for false, 1 for true)
- * Default: {@value #DEFAULT_ENABLE_RESTRICTED_BUCKET}
- *
- * @hide
- */
- @Readable
- public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket";
-
- /**
- * @see #ENABLE_RESTRICTED_BUCKET
- * @hide
- */
- public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1;
-
- /**
* Whether or not app auto restriction is enabled. When it is enabled, settings app will
* auto restrict the app if it has bad behavior (e.g. hold wakelock for long time).
*
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 6f17b87..8072330 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -359,16 +359,6 @@
Log.e(TAG, "Failed to get capabilities: ", e);
}
- try {
- String[] discovered =
- metadata.getStringArray(CredentialProviderService.CAPABILITY_META_DATA_KEY);
- if (discovered != null) {
- capabilities.addAll(Arrays.asList(discovered));
- }
- } catch (Resources.NotFoundException | NullPointerException e) {
- Log.e(TAG, "Failed to get capabilities: ", e);
- }
-
if (capabilities.size() == 0) {
Log.e(TAG, "No capabilities found for provider:" + serviceInfo);
return output;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 8d95c02..0b947fc 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -60,6 +60,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -97,6 +98,7 @@
import android.window.ClientWindowFrames;
import android.window.ScreenCapture;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
@@ -104,9 +106,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -167,11 +170,12 @@
private static final int MSG_REPORT_SHOWN = 10150;
private static final int MSG_UPDATE_DIMMING = 10200;
private static final int MSG_WALLPAPER_FLAGS_CHANGED = 10210;
- private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
- Float.NEGATIVE_INFINITY);
+ /** limit calls to {@link Engine#onComputeColors} to at most once per second */
private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
- private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 1000;
+
+ /** limit calls to {@link Engine#processLocalColorsInternal} to at most once per 2 seconds */
+ private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 2000;
private static final boolean ENABLE_WALLPAPER_DIMMING =
SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true);
@@ -180,6 +184,9 @@
private final ArrayMap<IBinder, IWallpaperEngineWrapper> mActiveEngines = new ArrayMap<>();
+ private Handler mBackgroundHandler;
+ private HandlerThread mBackgroundThread;
+
static final class WallpaperCommand {
String action;
int x;
@@ -198,14 +205,6 @@
*/
public class Engine {
IWallpaperEngineWrapper mIWallpaperEngine;
- final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
- final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
-
- // 2D matrix [x][y] to represent a page of a portion of a window
- EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
- Bitmap mLastScreenshot;
- int mLastWindowPage = -1;
- private boolean mResetWindowPages;
// Copies from mIWallpaperEngine.
HandlerCaller mCaller;
@@ -266,11 +265,34 @@
final Object mLock = new Object();
boolean mOffsetMessageEnqueued;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- float mPendingXOffset;
- float mPendingYOffset;
- float mPendingXOffsetStep;
- float mPendingYOffsetStep;
+ @GuardedBy("mLock")
+ private float mPendingXOffset;
+ @GuardedBy("mLock")
+ private float mPendingYOffset;
+ @GuardedBy("mLock")
+ private float mPendingXOffsetStep;
+ @GuardedBy("mLock")
+ private float mPendingYOffsetStep;
+
+ /**
+ * local color extraction related fields. When a user calls `addLocalColorAreas`
+ */
+ @GuardedBy("mLock")
+ private final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+
+ @GuardedBy("mLock")
+ private final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+ private long mLastProcessLocalColorsTimestamp;
+ private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
+ private int mPixelCopyCount = 0;
+ // 2D matrix [x][y] to represent a page of a portion of a window
+ @GuardedBy("mLock")
+ private EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+ private Bitmap mLastScreenshot;
+ private boolean mResetWindowPages;
+
boolean mPendingSync;
MotionEvent mPendingMove;
boolean mIsInAmbientMode;
@@ -279,12 +301,8 @@
private long mLastColorInvalidation;
private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
- // used to throttle processLocalColors
- private long mLastProcessLocalColorsTimestamp;
- private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
-
private Display mDisplay;
private Context mDisplayContext;
private int mDisplayState;
@@ -854,7 +872,7 @@
+ "was not established.");
}
mResetWindowPages = true;
- processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+ processLocalColors();
} catch (RemoteException e) {
Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
}
@@ -1389,10 +1407,9 @@
mIsCreating = false;
mSurfaceCreated = true;
if (redrawNeeded) {
- resetWindowPages();
mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
Integer.MAX_VALUE);
- processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+ processLocalColors();
}
reposition();
reportEngineShown(shouldWaitForEngineShown());
@@ -1536,7 +1553,7 @@
if (!mDestroyed) {
mVisible = visible;
reportVisibility(false);
- if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+ if (mReportedVisible) processLocalColors();
} else {
AnimationHandler.requestAnimatorsEnabled(visible, this);
}
@@ -1640,14 +1657,14 @@
}
// setup local color extraction data
- processLocalColors(xOffset, xOffsetStep);
+ processLocalColors();
}
/**
* Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
* {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
*/
- private void processLocalColors(float xOffset, float xOffsetStep) {
+ private void processLocalColors() {
if (mProcessLocalColorsPending.compareAndSet(false, true)) {
final long now = mClockFunction.get();
final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
@@ -1657,80 +1674,98 @@
mHandler.postDelayed(() -> {
mLastProcessLocalColorsTimestamp = now + timeToWait;
mProcessLocalColorsPending.set(false);
- processLocalColorsInternal(xOffset, xOffsetStep);
+ processLocalColorsInternal();
}, timeToWait);
}
}
- private void processLocalColorsInternal(float xOffset, float xOffsetStep) {
- // implemented by the wallpaper
+ /**
+ * Default implementation of the local color extraction.
+ * This will take a screenshot of the whole wallpaper on the main thread.
+ * Then, in a background thread, for each launcher page, for each area that needs color
+ * extraction in this page, creates a sub-bitmap and call {@link WallpaperColors#fromBitmap}
+ * to extract the colors. Every time a launcher page has been processed, call
+ * {@link #notifyLocalColorsChanged} with the color and areas of this page.
+ */
+ private void processLocalColorsInternal() {
if (supportsLocalColorExtraction()) return;
- if (DEBUG) {
- Log.d(TAG, "processLocalColors " + xOffset + " of step "
- + xOffsetStep);
- }
- //below is the default implementation
- if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN
- || !mSurfaceHolder.getSurface().isValid()) return;
- int xCurrentPage;
+ float xOffset;
+ float xOffsetStep;
+ float wallpaperDimAmount;
+ int xPage;
int xPages;
- if (!validStep(xOffsetStep)) {
- if (DEBUG) {
- Log.w(TAG, "invalid offset step " + xOffsetStep);
- }
- xOffset = 0;
- xOffsetStep = 1;
- xCurrentPage = 0;
- xPages = 1;
- } else {
- xPages = Math.round(1 / xOffsetStep) + 1;
- xOffsetStep = (float) 1 / (float) xPages;
- float shrink = (float) (xPages - 1) / (float) xPages;
- xOffset *= shrink;
- xCurrentPage = Math.round(xOffset / xOffsetStep);
- }
- if (DEBUG) {
- Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage);
- Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
- }
-
- float finalXOffsetStep = xOffsetStep;
- float finalXOffset = xOffset;
-
- Trace.beginSection("WallpaperService#processLocalColors");
- resetWindowPages();
- int xPage = xCurrentPage;
+ Set<RectF> areas;
EngineWindowPage current;
- if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
- mWindowPages = new EngineWindowPage[xPages];
- initWindowPages(mWindowPages, finalXOffsetStep);
- }
- if (mLocalColorsToAdd.size() != 0) {
- for (RectF colorArea : mLocalColorsToAdd) {
- if (!isValid(colorArea)) continue;
- mLocalColorAreas.add(colorArea);
- int colorPage = getRectFPage(colorArea, finalXOffsetStep);
- EngineWindowPage currentPage = mWindowPages[colorPage];
- currentPage.setLastUpdateTime(0);
- currentPage.removeColor(colorArea);
- }
- mLocalColorsToAdd.clear();
- }
- if (xPage >= mWindowPages.length) {
+
+ synchronized (mLock) {
+ xOffset = mPendingXOffset;
+ xOffsetStep = mPendingXOffsetStep;
+ wallpaperDimAmount = mWallpaperDimAmount;
+
if (DEBUG) {
- Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
- Log.e(TAG, "error on page " + xPage + " out of " + xPages);
- Log.e(TAG,
- "error on xOffsetStep " + finalXOffsetStep
- + " xOffset " + finalXOffset);
+ Log.d(TAG, "processLocalColors " + xOffset + " of step "
+ + xOffsetStep);
}
- xPage = mWindowPages.length - 1;
+ if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN
+ || !mSurfaceHolder.getSurface().isValid()) return;
+ int xCurrentPage;
+ if (!validStep(xOffsetStep)) {
+ if (DEBUG) {
+ Log.w(TAG, "invalid offset step " + xOffsetStep);
+ }
+ xOffset = 0;
+ xOffsetStep = 1;
+ xCurrentPage = 0;
+ xPages = 1;
+ } else {
+ xPages = Math.round(1 / xOffsetStep) + 1;
+ xOffsetStep = (float) 1 / (float) xPages;
+ float shrink = (float) (xPages - 1) / (float) xPages;
+ xOffset *= shrink;
+ xCurrentPage = Math.round(xOffset / xOffsetStep);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage);
+ Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
+ }
+
+ float finalXOffsetStep = xOffsetStep;
+ float finalXOffset = xOffset;
+
+ resetWindowPages();
+ xPage = xCurrentPage;
+ if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
+ mWindowPages = new EngineWindowPage[xPages];
+ initWindowPages(mWindowPages, finalXOffsetStep);
+ }
+ if (mLocalColorsToAdd.size() != 0) {
+ for (RectF colorArea : mLocalColorsToAdd) {
+ if (!isValid(colorArea)) continue;
+ mLocalColorAreas.add(colorArea);
+ int colorPage = getRectFPage(colorArea, finalXOffsetStep);
+ EngineWindowPage currentPage = mWindowPages[colorPage];
+ currentPage.setLastUpdateTime(0);
+ currentPage.removeColor(colorArea);
+ }
+ mLocalColorsToAdd.clear();
+ }
+ if (xPage >= mWindowPages.length) {
+ if (DEBUG) {
+ Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
+ Log.e(TAG, "error on page " + xPage + " out of " + xPages);
+ Log.e(TAG,
+ "error on xOffsetStep " + finalXOffsetStep
+ + " xOffset " + finalXOffset);
+ }
+ xPage = mWindowPages.length - 1;
+ }
+ current = mWindowPages[xPage];
+ areas = new HashSet<>(current.getAreas());
}
- current = mWindowPages[xPage];
- updatePage(current, xPage, xPages, finalXOffsetStep);
- Trace.endSection();
+ updatePage(current, areas, xPage, xPages, wallpaperDimAmount);
}
+ @GuardedBy("mLock")
private void initWindowPages(EngineWindowPage[] windowPages, float step) {
for (int i = 0; i < windowPages.length; i++) {
windowPages[i] = new EngineWindowPage();
@@ -1747,16 +1782,16 @@
}
}
- void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
- float xOffsetStep) {
+ void updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages,
+ float wallpaperDimAmount) {
+
// in case the clock is zero, we start with negative time
long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
long lapsed = current - currentPage.getLastUpdateTime();
// Always update the page when the last update time is <= 0
// This is important especially when the device first boots
- if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
- return;
- }
+ if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
+
Surface surface = mSurfaceHolder.getSurface();
if (!surface.isValid()) return;
boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1769,43 +1804,59 @@
Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height);
return;
}
+ final String pixelCopySectionName = "WallpaperService#pixelCopy";
+ final int pixelCopyCount = mPixelCopyCount++;
+ Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
Bitmap screenShot = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
final Bitmap finalScreenShot = screenShot;
- Trace.beginSection("WallpaperService#pixelCopy");
- PixelCopy.request(surface, screenShot, (res) -> {
- Trace.endSection();
- if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
- if (res != PixelCopy.SUCCESS) {
- Bitmap lastBitmap = currentPage.getBitmap();
- // assign the last bitmap taken for now
- currentPage.setBitmap(mLastScreenshot);
- Bitmap lastScreenshot = mLastScreenshot;
- if (lastScreenshot != null && !lastScreenshot.isRecycled()
- && !Objects.equals(lastBitmap, lastScreenshot)) {
- updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+ try {
+ // TODO(b/274427458) check if this can be done in the background.
+ PixelCopy.request(surface, screenShot, (res) -> {
+ Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
+ if (DEBUG) {
+ Log.d(TAG, "result of pixel copy is: "
+ + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE"));
}
- } else {
- mLastScreenshot = finalScreenShot;
- // going to hold this lock for a while
- currentPage.setBitmap(finalScreenShot);
- currentPage.setLastUpdateTime(current);
- updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
- }
- }, mHandler);
-
+ if (res != PixelCopy.SUCCESS) {
+ Bitmap lastBitmap = currentPage.getBitmap();
+ // assign the last bitmap taken for now
+ currentPage.setBitmap(mLastScreenshot);
+ Bitmap lastScreenshot = mLastScreenshot;
+ if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) {
+ updatePageColors(
+ currentPage, areas, pageIndx, numPages, wallpaperDimAmount);
+ }
+ } else {
+ mLastScreenshot = finalScreenShot;
+ currentPage.setBitmap(finalScreenShot);
+ currentPage.setLastUpdateTime(current);
+ updatePageColors(
+ currentPage, areas, pageIndx, numPages, wallpaperDimAmount);
+ }
+ }, mBackgroundHandler);
+ } catch (IllegalArgumentException e) {
+ // this can potentially happen if the surface is invalidated right between the
+ // surface.isValid() check and the PixelCopy operation.
+ // in this case, stop: we'll compute colors on the next processLocalColors call.
+ Log.w(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
+ }
}
// locked by the passed page
- private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
- float xOffsetStep) {
+ private void updatePageColors(EngineWindowPage page, Set<RectF> areas,
+ int pageIndx, int numPages, float wallpaperDimAmount) {
if (page.getBitmap() == null) return;
+ if (!mBackgroundHandler.getLooper().isCurrentThread()) {
+ throw new IllegalStateException(
+ "ProcessLocalColors should be called from the background thread");
+ }
Trace.beginSection("WallpaperService#updatePageColors");
if (DEBUG) {
Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
+ page.getAreas().size() + " and bitmap size of "
+ page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight());
}
- for (RectF area: page.getAreas()) {
+ for (RectF area: areas) {
if (area == null) continue;
RectF subArea = generateSubRect(area, pageIndx, numPages);
Bitmap b = page.getBitmap();
@@ -1815,12 +1866,12 @@
int height = Math.round(b.getHeight() * subArea.height());
Bitmap target;
try {
- target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height);
+ target = Bitmap.createBitmap(b, x, y, width, height);
} catch (Exception e) {
Log.e(TAG, "Error creating page local color bitmap", e);
continue;
}
- WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
+ WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
target.recycle();
WallpaperColors currentColor = page.getColors(area);
@@ -1837,12 +1888,14 @@
+ " local color callback for area" + area + " for page " + pageIndx
+ " of " + numPages);
}
- try {
- mConnection.onLocalWallpaperColorsChanged(area, color,
- mDisplayContext.getDisplayId());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
- }
+ mHandler.post(() -> {
+ try {
+ mConnection.onLocalWallpaperColorsChanged(area, color,
+ mDisplayContext.getDisplayId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+ }
+ });
}
}
Trace.endSection();
@@ -1868,16 +1921,17 @@
return new RectF(left, in.top, right, in.bottom);
}
+ @GuardedBy("mLock")
private void resetWindowPages() {
if (supportsLocalColorExtraction()) return;
if (!mResetWindowPages) return;
mResetWindowPages = false;
- mLastWindowPage = -1;
for (int i = 0; i < mWindowPages.length; i++) {
mWindowPages[i].setLastUpdateTime(0L);
}
}
+ @GuardedBy("mLock")
private int getRectFPage(RectF area, float step) {
if (!isValid(area)) return 0;
if (!validStep(step)) return 0;
@@ -1898,12 +1952,12 @@
if (DEBUG) {
Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
}
- mHandler.post(() -> {
- mLocalColorsToAdd.addAll(regions);
- processLocalColors(mPendingXOffset, mPendingYOffset);
+ mBackgroundHandler.post(() -> {
+ synchronized (mLock) {
+ mLocalColorsToAdd.addAll(regions);
+ }
+ processLocalColors();
});
-
-
}
/**
@@ -1913,16 +1967,18 @@
*/
public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
if (supportsLocalColorExtraction()) return;
- mHandler.post(() -> {
- float step = mPendingXOffsetStep;
- mLocalColorsToAdd.removeAll(regions);
- mLocalColorAreas.removeAll(regions);
- if (!validStep(step)) {
- return;
- }
- for (int i = 0; i < mWindowPages.length; i++) {
- for (int j = 0; j < regions.size(); j++) {
- mWindowPages[i].removeArea(regions.get(j));
+ mBackgroundHandler.post(() -> {
+ synchronized (mLock) {
+ float step = mPendingXOffsetStep;
+ mLocalColorsToAdd.removeAll(regions);
+ mLocalColorAreas.removeAll(regions);
+ if (!validStep(step)) {
+ return;
+ }
+ for (int i = 0; i < mWindowPages.length; i++) {
+ for (int j = 0; j < regions.size(); j++) {
+ mWindowPages[i].removeArea(regions.get(j));
+ }
}
}
});
@@ -1940,7 +1996,7 @@
}
private boolean validStep(float step) {
- return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.;
+ return !Float.isNaN(step) && step > 0f && step <= 1f;
}
void doCommand(WallpaperCommand cmd) {
@@ -2579,6 +2635,9 @@
@Override
public void onCreate() {
Trace.beginSection("WPMS.onCreate");
+ mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
super.onCreate();
Trace.endSection();
}
@@ -2591,6 +2650,7 @@
engineWrapper.destroy();
}
mActiveEngines.clear();
+ mBackgroundThread.quitSafely();
Trace.endSection();
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6201b3a..4c6bd67 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -234,8 +234,8 @@
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true");
- DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "false");
- DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
+ DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index cc83dec..cd03d83 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -71,7 +71,7 @@
* Creates a new SparseArray containing no mappings.
*/
public SparseArray() {
- this(10);
+ this(0);
}
/**
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index c145b20..12a9900 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -51,7 +51,7 @@
* Creates a new SparseBooleanArray containing no mappings.
*/
public SparseBooleanArray() {
- this(10);
+ this(0);
}
/**
diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java
index ee2e3ce..4b0cbe4 100644
--- a/core/java/android/util/SparseDoubleArray.java
+++ b/core/java/android/util/SparseDoubleArray.java
@@ -50,7 +50,7 @@
/** Creates a new SparseDoubleArray containing no mappings. */
public SparseDoubleArray() {
- this(10);
+ this(0);
}
/**
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index d4f6685..0e98c28 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -58,7 +58,7 @@
* Creates a new SparseIntArray containing no mappings.
*/
public SparseIntArray() {
- this(10);
+ this(0);
}
/**
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index b739e37..e86b647 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -51,7 +51,7 @@
* Creates a new SparseLongArray containing no mappings.
*/
public SparseLongArray() {
- this(10);
+ this(0);
}
/**
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9225cd9..61864d7 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -1183,8 +1183,10 @@
*/
@NonNull
public LightsManager getLightsManager() {
- if (mLightsManager == null) {
- mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
+ synchronized (mMotionRanges) {
+ if (mLightsManager == null) {
+ mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
+ }
}
return mLightsManager;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b46a68c..d8b6b7b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -851,10 +851,14 @@
}
mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
- if (mViewVisibility) {
- surfaceUpdateTransaction.show(mSurfaceControl);
- } else {
- surfaceUpdateTransaction.hide(mSurfaceControl);
+ // Only control visibility if we're not hardware-accelerated. Otherwise we'll
+ // let renderthread drive since offscreen SurfaceControls should not be visible.
+ if (!isHardwareAccelerated()) {
+ if (mViewVisibility) {
+ surfaceUpdateTransaction.show(mSurfaceControl);
+ } else {
+ surfaceUpdateTransaction.hide(mSurfaceControl);
+ }
}
updateBackgroundVisibility(surfaceUpdateTransaction);
@@ -1417,12 +1421,10 @@
}
private final Rect mRTLastReportedPosition = new Rect();
- private final Point mRTLastReportedSurfaceSize = new Point();
private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
private final int mRtSurfaceWidth;
private final int mRtSurfaceHeight;
- private boolean mRtFirst = true;
private final SurfaceControl.Transaction mPositionChangedTransaction =
new SurfaceControl.Transaction();
@@ -1433,15 +1435,6 @@
@Override
public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
- if (!mRtFirst && (mRTLastReportedPosition.left == left
- && mRTLastReportedPosition.top == top
- && mRTLastReportedPosition.right == right
- && mRTLastReportedPosition.bottom == bottom
- && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
- && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight)) {
- return;
- }
- mRtFirst = false;
try {
if (DEBUG_POSITION) {
Log.d(TAG, String.format(
@@ -1452,8 +1445,8 @@
}
synchronized (mSurfaceControlLock) {
if (mSurfaceControl == null) return;
+
mRTLastReportedPosition.set(left, top, right, bottom);
- mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight);
onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl,
mRTLastReportedPosition.left /*positionLeft*/,
mRTLastReportedPosition.top /*positionTop*/,
@@ -1461,10 +1454,8 @@
/ (float) mRtSurfaceWidth /*postScaleX*/,
mRTLastReportedPosition.height()
/ (float) mRtSurfaceHeight /*postScaleY*/);
- if (mViewVisibility) {
- // b/131239825
- mPositionChangedTransaction.show(mSurfaceControl);
- }
+
+ mPositionChangedTransaction.show(mSurfaceControl);
}
applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
} catch (Exception ex) {
@@ -1490,7 +1481,6 @@
System.identityHashCode(this), frameNumber));
}
mRTLastReportedPosition.setEmpty();
- mRTLastReportedSurfaceSize.set(-1, -1);
// positionLost can be called while UI thread is un-paused.
synchronized (mSurfaceControlLock) {
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 02dafd4..7f0a651 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -13,3 +13,5 @@
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
per-file RemoteViews* = file:../appwidget/OWNERS
+
+per-file Toast.java = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 0d02683..a1d571f 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -1,4 +1,6 @@
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file UnlaunchableAppActivity.java = file:/core/java/android/app/admin/WorkProfile_OWNERS
+per-file IntentForwarderActivity.java = file:/core/java/android/app/admin/WorkProfile_OWNERS
per-file *Resolver* = file:/packages/SystemUI/OWNERS
per-file *Chooser* = file:/packages/SystemUI/OWNERS
per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 9fae211..2b08a55 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -74,6 +74,9 @@
public static final Flag OTP_REDACTION =
devFlag("persist.sysui.notification.otp_redaction");
+ /** Gating the removal of sorting-notifications-by-interruptiveness. */
+ public static final Flag NO_SORT_BY_INTERRUPTIVENESS =
+ devFlag("persist.sysui.notification.no_sort_by_interruptiveness");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java
index afdbdc8..4a46d91 100644
--- a/core/java/com/android/internal/expresslog/Counter.java
+++ b/core/java/com/android/internal/expresslog/Counter.java
@@ -36,6 +36,16 @@
}
/**
+ * Increments Telemetry Express Counter metric by 1
+ * @param metricId to log, no-op if metricId is not defined in the TeX catalog
+ * @param uid used as a dimension for the count metric
+ * @hide
+ */
+ public static void logIncrementWithUid(@NonNull String metricId, int uid) {
+ logIncrementWithUid(metricId, uid, 1);
+ }
+
+ /**
* Increments Telemetry Express Counter metric by arbitrary value
* @param metricId to log, no-op if metricId is not defined in the TeX catalog
* @param amount to increment counter
@@ -45,4 +55,17 @@
final long metricIdHash = Utils.hashString(metricId);
FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
}
+
+ /**
+ * Increments Telemetry Express Counter metric by arbitrary value
+ * @param metricId to log, no-op if metricId is not defined in the TeX catalog
+ * @param uid used as a dimension for the count metric
+ * @param amount to increment counter
+ * @hide
+ */
+ public static void logIncrementWithUid(@NonNull String metricId, int uid, long amount) {
+ final long metricIdHash = Utils.hashString(metricId);
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid);
+ }
}
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
index 096d1cd..8580c0c 100644
--- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -18,6 +18,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
import static com.android.internal.os.TimeoutRecord.TimeoutKind;
import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__BROADCAST_OF_INTENT;
import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__CONTENT_PROVIDER_NOT_RESPONDING;
@@ -28,12 +29,15 @@
import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__START_FOREGROUND_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__UNKNOWN_ANR_TYPE;
+import android.annotation.IntDef;
import android.os.SystemClock;
import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -44,6 +48,22 @@
*/
public class AnrLatencyTracker implements AutoCloseable {
+ /** Status of the early dumped pid. */
+ @IntDef(value = {
+ EarlyDumpStatus.UNKNOWN,
+ EarlyDumpStatus.SUCCEEDED,
+ EarlyDumpStatus.FAILED_TO_CREATE_FILE,
+ EarlyDumpStatus.TIMED_OUT
+ })
+
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface EarlyDumpStatus {
+ int UNKNOWN = 1;
+ int SUCCEEDED = 2;
+ int FAILED_TO_CREATE_FILE = 3;
+ int TIMED_OUT = 4;
+ }
+
private static final AtomicInteger sNextAnrRecordPlacedOnQueueCookieGenerator =
new AtomicInteger();
@@ -77,7 +97,16 @@
private int mAnrQueueSize;
private int mAnrType;
- private int mDumpedProcessesCount = 0;
+ private final AtomicInteger mDumpedProcessesCount = new AtomicInteger(0);
+
+ private volatile @EarlyDumpStatus int mEarlyDumpStatus =
+ EarlyDumpStatus.UNKNOWN;
+ private volatile long mTempFileDumpingStartUptime;
+ private volatile long mTempFileDumpingDuration = 0;
+ private long mCopyingFirstPidStartUptime;
+ private long mCopyingFirstPidDuration = 0;
+ private long mEarlyDumpRequestSubmissionUptime = 0;
+ private long mEarlyDumpExecutorPidCount = 0;
private long mFirstPidsDumpingStartUptime;
private long mFirstPidsDumpingDuration = 0;
@@ -88,7 +117,7 @@
private boolean mIsPushed = false;
private boolean mIsSkipped = false;
-
+ private boolean mCopyingFirstPidSucceeded = false;
private final int mAnrRecordPlacedOnQueueCookie =
sNextAnrRecordPlacedOnQueueCookieGenerator.incrementAndGet();
@@ -111,6 +140,15 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
+ /**
+ * Records the number of processes we are currently early-dumping, this number includes the
+ * current ANR's main process.
+ */
+ public void earlyDumpRequestSubmittedWithSize(int currentProcessedPidCount) {
+ mEarlyDumpRequestSubmissionUptime = getUptimeMillis();
+ mEarlyDumpExecutorPidCount = currentProcessedPidCount;
+ }
+
/** Records the placing of the AnrHelper.AnrRecord instance on the processing queue. */
public void anrRecordPlacingOnQueueWithSize(int queueSize) {
mAnrRecordPlacedOnQueueUptime = getUptimeMillis();
@@ -210,48 +248,89 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ /** Records the start of pid dumping to file. */
public void dumpingPidStarted(int pid) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingPid#" + pid);
}
- /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ /** Records the end of pid dumping to file. */
public void dumpingPidEnded() {
- mDumpedProcessesCount++;
+ mDumpedProcessesCount.incrementAndGet();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ /** Records the start of first pids dumping to file. */
public void dumpingFirstPidsStarted() {
mFirstPidsDumpingStartUptime = getUptimeMillis();
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingFirstPids");
}
- /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ /** Records the end of first pids dumping to file. */
public void dumpingFirstPidsEnded() {
mFirstPidsDumpingDuration = getUptimeMillis() - mFirstPidsDumpingStartUptime;
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- /** Records the start of pid dumping to file (subject and criticalEventSection). */
+
+ /** Records the start of the copying of the pre-dumped first pid. */
+ public void copyingFirstPidStarted() {
+ mCopyingFirstPidStartUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "copyingFirstPid");
+ }
+
+ /** Records the end of the copying of the pre-dumped first pid. */
+ public void copyingFirstPidEnded(boolean copySucceeded) {
+ mCopyingFirstPidDuration = getUptimeMillis() - mCopyingFirstPidStartUptime;
+ mCopyingFirstPidSucceeded = copySucceeded;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of pre-dumping. */
+ public void dumpStackTracesTempFileStarted() {
+ mTempFileDumpingStartUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpStackTracesTempFile");
+ }
+
+ /** Records the end of pre-dumping. */
+ public void dumpStackTracesTempFileEnded() {
+ mTempFileDumpingDuration = getUptimeMillis() - mTempFileDumpingStartUptime;
+ if (mEarlyDumpStatus == EarlyDumpStatus.UNKNOWN) {
+ mEarlyDumpStatus = EarlyDumpStatus.SUCCEEDED;
+ }
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records file creation failure events in dumpStackTracesTempFile. */
+ public void dumpStackTracesTempFileCreationFailed() {
+ mEarlyDumpStatus = EarlyDumpStatus.FAILED_TO_CREATE_FILE;
+ Trace.instant(TRACE_TAG_ACTIVITY_MANAGER, "dumpStackTracesTempFileCreationFailed");
+ }
+
+ /** Records timeout events in dumpStackTracesTempFile. */
+ public void dumpStackTracesTempFileTimedOut() {
+ mEarlyDumpStatus = EarlyDumpStatus.TIMED_OUT;
+ Trace.instant(TRACE_TAG_ACTIVITY_MANAGER, "dumpStackTracesTempFileTimedOut");
+ }
+
+ /** Records the start of native pids dumping to file. */
public void dumpingNativePidsStarted() {
mNativePidsDumpingStartUptime = getUptimeMillis();
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingNativePids");
}
- /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ /** Records the end of native pids dumping to file . */
public void dumpingNativePidsEnded() {
mNativePidsDumpingDuration = getUptimeMillis() - mNativePidsDumpingStartUptime;
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ /** Records the start of extra pids dumping to file. */
public void dumpingExtraPidsStarted() {
mExtraPidsDumpingStartUptime = getUptimeMillis();
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingExtraPids");
}
- /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ /** Records the end of extra pids dumping to file. */
public void dumpingExtraPidsEnded() {
mExtraPidsDumpingDuration = getUptimeMillis() - mExtraPidsDumpingStartUptime;
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -337,7 +416,7 @@
* Returns latency data as a comma separated value string for inclusion in ANR report.
*/
public String dumpAsCommaSeparatedArrayWithHeader() {
- return "DurationsV2: " + mAnrTriggerUptime
+ return "DurationsV3: " + mAnrTriggerUptime
/* triggering_to_app_not_responding_duration = */
+ "," + (mAppNotRespondingStartUptime - mAnrTriggerUptime)
/* app_not_responding_duration = */
@@ -370,7 +449,22 @@
/* anr_queue_size_when_pushed = */
+ "," + mAnrQueueSize
/* dump_stack_traces_io_time = */
- + "," + (mFirstPidsDumpingStartUptime - mDumpStackTracesStartUptime)
+ // We use copyingFirstPidUptime if we're dumping the durations list before the
+ // first pids ie after copying the early dump stacks.
+ + "," + ((mFirstPidsDumpingStartUptime > 0 ? mFirstPidsDumpingStartUptime
+ : mCopyingFirstPidStartUptime) - mDumpStackTracesStartUptime)
+ /* temp_file_dump_duration = */
+ + "," + mTempFileDumpingDuration
+ /* temp_dump_request_on_queue_duration = */
+ + "," + (mTempFileDumpingStartUptime - mEarlyDumpRequestSubmissionUptime)
+ /* temp_dump_pid_count_when_pushed = */
+ + "," + mEarlyDumpExecutorPidCount
+ /* first_pid_copying_time = */
+ + "," + mCopyingFirstPidDuration
+ /* early_dump_status = */
+ + "," + mEarlyDumpStatus
+ /* copying_first_pid_succeeded = */
+ + "," + (mCopyingFirstPidSucceeded ? 1 : 0)
+ "\n\n";
}
@@ -449,7 +543,7 @@
/* anr_queue_size_when_pushed = */ mAnrQueueSize,
/* anr_type = */ mAnrType,
- /* dumped_processes_count = */ mDumpedProcessesCount);
+ /* dumped_processes_count = */ mDumpedProcessesCount.get());
}
private void anrSkipped(String method) {
diff --git a/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS b/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
new file mode 100644
index 0000000..7755000
--- /dev/null
+++ b/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
@@ -0,0 +1,3 @@
+# TODO(b/274465475): Migrate LatencyTracker testing to its own module
+marcinoc@google.com
+ilkos@google.com
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 0defb40..c144503 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -49,6 +49,7 @@
import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
import android.Manifest;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -56,7 +57,6 @@
import android.app.ActivityThread;
import android.content.Context;
import android.os.Build;
-import android.os.ConditionVariable;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.DeviceConfig;
@@ -80,7 +80,7 @@
* Class to track various latencies in SystemUI. It then writes the latency to statsd and also
* outputs it to logcat so these latencies can be captured by tests and then used for dashboards.
* <p>
- * This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but
+ * This is currently only in Keyguard. It can be shared between SystemUI and Keyguard, but
* eventually we'd want to merge these two packages together so Keyguard can use common classes
* that are shared with SystemUI.
*/
@@ -298,8 +298,6 @@
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
};
- private static LatencyTracker sLatencyTracker;
-
private final Object mLock = new Object();
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
@@ -307,20 +305,21 @@
private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
@GuardedBy("mLock")
private boolean mEnabled;
- @VisibleForTesting
- public final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();
- public static LatencyTracker getInstance(Context context) {
- if (sLatencyTracker == null) {
- synchronized (LatencyTracker.class) {
- if (sLatencyTracker == null) {
- sLatencyTracker = new LatencyTracker();
- }
- }
- }
- return sLatencyTracker;
+ // Wrapping this in a holder class achieves lazy loading behavior
+ private static final class SLatencyTrackerHolder {
+ private static final LatencyTracker sLatencyTracker = new LatencyTracker();
}
+ public static LatencyTracker getInstance(Context context) {
+ return SLatencyTrackerHolder.sLatencyTracker;
+ }
+
+ /**
+ * Constructor for LatencyTracker
+ *
+ * <p>This constructor is only visible for test classes to inject their own consumer callbacks
+ */
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
@VisibleForTesting
public LatencyTracker() {
@@ -362,11 +361,8 @@
properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
legacyActionTraceThreshold)));
}
- if (DEBUG) {
- Log.d(TAG, "updated action properties: " + mActionPropertiesMap);
- }
+ onDeviceConfigPropertiesUpdated(mActionPropertiesMap);
}
- mDeviceConfigPropertiesUpdated.open();
}
/**
@@ -492,7 +488,7 @@
*/
public void onActionStart(@Action int action, String tag) {
synchronized (mLock) {
- if (!isEnabled()) {
+ if (!isEnabled(action)) {
return;
}
// skip if the action is already instrumenting.
@@ -516,7 +512,7 @@
*/
public void onActionEnd(@Action int action) {
synchronized (mLock) {
- if (!isEnabled()) {
+ if (!isEnabled(action)) {
return;
}
Session session = mSessions.get(action);
@@ -555,6 +551,24 @@
}
/**
+ * Testing API to get the time when a given action was started.
+ *
+ * @param action Action which to retrieve start time from
+ * @return Elapsed realtime timestamp when the action started. -1 if the action is not active.
+ * @hide
+ */
+ @VisibleForTesting
+ @ElapsedRealtimeLong
+ public long getActiveActionStartTime(@Action int action) {
+ synchronized (mLock) {
+ if (mSessions.contains(action)) {
+ return mSessions.get(action).mStartRtc;
+ }
+ return -1;
+ }
+ }
+
+ /**
* Logs an action that has started and ended. This needs to be called from the main thread.
*
* @param action The action to end. One of the ACTION_* values.
@@ -564,6 +578,9 @@
boolean shouldSample;
int traceThreshold;
synchronized (mLock) {
+ if (!isEnabled(action)) {
+ return;
+ }
ActionProperties actionProperties = mActionPropertiesMap.get(action);
if (actionProperties == null) {
return;
@@ -574,28 +591,24 @@
traceThreshold = actionProperties.getTraceThreshold();
}
- if (traceThreshold > 0 && duration >= traceThreshold) {
- PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
+ boolean shouldTriggerPerfettoTrace = traceThreshold > 0 && duration >= traceThreshold;
+
+ if (DEBUG) {
+ Log.i(TAG, "logAction: " + getNameOfAction(STATSD_ACTION[action])
+ + " duration=" + duration
+ + " shouldSample=" + shouldSample
+ + " shouldTriggerPerfettoTrace=" + shouldTriggerPerfettoTrace);
}
- logActionDeprecated(action, duration, shouldSample);
- }
-
- /**
- * Logs an action that has started and ended. This needs to be called from the main thread.
- *
- * @param action The action to end. One of the ACTION_* values.
- * @param duration The duration of the action in ms.
- * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
- */
- public static void logActionDeprecated(
- @Action int action, int duration, boolean writeToStatsLog) {
- Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
-
- if (writeToStatsLog) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
+ if (shouldTriggerPerfettoTrace) {
+ onTriggerPerfetto(getTraceTriggerNameForAction(action));
+ }
+ if (shouldSample) {
+ onLogToFrameworkStats(
+ new FrameworkStatsLogEvent(action, FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED,
+ STATSD_ACTION[action], duration)
+ );
}
}
@@ -657,10 +670,10 @@
}
@VisibleForTesting
- static class ActionProperties {
+ public static class ActionProperties {
static final String ENABLE_SUFFIX = "_enable";
static final String SAMPLE_INTERVAL_SUFFIX = "_sample_interval";
- // TODO: migrate all usages of the legacy trace theshold property
+ // TODO: migrate all usages of the legacy trace threshold property
static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";
@@ -670,7 +683,8 @@
private final int mSamplingInterval;
private final int mTraceThreshold;
- ActionProperties(
+ @VisibleForTesting
+ public ActionProperties(
@Action int action,
boolean enabled,
int samplingInterval,
@@ -683,20 +697,24 @@
this.mTraceThreshold = traceThreshold;
}
+ @VisibleForTesting
@Action
- int getAction() {
+ public int getAction() {
return mAction;
}
- boolean isEnabled() {
+ @VisibleForTesting
+ public boolean isEnabled() {
return mEnabled;
}
- int getSamplingInterval() {
+ @VisibleForTesting
+ public int getSamplingInterval() {
return mSamplingInterval;
}
- int getTraceThreshold() {
+ @VisibleForTesting
+ public int getTraceThreshold() {
return mTraceThreshold;
}
@@ -709,5 +727,103 @@
+ ", mTraceThreshold=" + mTraceThreshold
+ "}";
}
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof ActionProperties)) {
+ return false;
+ }
+ ActionProperties that = (ActionProperties) o;
+ return mAction == that.mAction
+ && mEnabled == that.mEnabled
+ && mSamplingInterval == that.mSamplingInterval
+ && mTraceThreshold == that.mTraceThreshold;
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mAction;
+ _hash = 31 * _hash + Boolean.hashCode(mEnabled);
+ _hash = 31 * _hash + mSamplingInterval;
+ _hash = 31 * _hash + mTraceThreshold;
+ return _hash;
+ }
+ }
+
+ /**
+ * Testing method intended to be overridden to determine when the LatencyTracker's device
+ * properties are updated.
+ */
+ @VisibleForTesting
+ public void onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties) {
+ if (DEBUG) {
+ Log.d(TAG, "onDeviceConfigPropertiesUpdated: " + actionProperties);
+ }
+ }
+
+ /**
+ * Testing class intended to be overridden to determine when LatencyTracker triggers perfetto.
+ */
+ @VisibleForTesting
+ public void onTriggerPerfetto(String triggerName) {
+ if (DEBUG) {
+ Log.i(TAG, "onTriggerPerfetto: triggerName=" + triggerName);
+ }
+ PerfettoTrigger.trigger(triggerName);
+ }
+
+ /**
+ * Testing method intended to be overridden to determine when LatencyTracker writes to
+ * FrameworkStatsLog.
+ */
+ @VisibleForTesting
+ public void onLogToFrameworkStats(FrameworkStatsLogEvent event) {
+ if (DEBUG) {
+ Log.i(TAG, "onLogToFrameworkStats: event=" + event);
+ }
+ FrameworkStatsLog.write(event.logCode, event.statsdAction, event.durationMillis);
+ }
+
+ /**
+ * Testing class intended to reject what should be written to the {@link FrameworkStatsLog}
+ *
+ * <p>This class is used in {@link #onLogToFrameworkStats(FrameworkStatsLogEvent)} for test code
+ * to observer when and what information is being logged by {@link LatencyTracker}
+ */
+ @VisibleForTesting
+ public static class FrameworkStatsLogEvent {
+
+ @VisibleForTesting
+ public final int action;
+ @VisibleForTesting
+ public final int logCode;
+ @VisibleForTesting
+ public final int statsdAction;
+ @VisibleForTesting
+ public final int durationMillis;
+
+ private FrameworkStatsLogEvent(int action, int logCode, int statsdAction,
+ int durationMillis) {
+ this.action = action;
+ this.logCode = logCode;
+ this.statsdAction = statsdAction;
+ this.durationMillis = durationMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "FrameworkStatsLogEvent{"
+ + " logCode=" + logCode
+ + ", statsdAction=" + statsdAction
+ + ", durationMillis=" + durationMillis
+ + "}";
+ }
}
}
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 1808bd5..9be8ea7 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -6,3 +6,4 @@
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file *Dump* = file:/core/java/com/android/internal/util/dump/OWNERS
per-file *Screenshot* = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
+per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 45d847d..5b43d62 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4482,6 +4482,12 @@
<permission android:name="android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows specifying candidate credential providers to be queried in Credential Manager
+ get flows, or to be preferred as a default in the Credential Manager create flows.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS"
+ android:protectionLevel="normal" />
+
<!-- Allows a browser to invoke credential manager APIs on behalf of another RP.
<p>Protection level: normal -->
<permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"
diff --git a/core/res/TEST_MAPPING b/core/res/TEST_MAPPING
index 9185bae..4d09076 100644
--- a/core/res/TEST_MAPPING
+++ b/core/res/TEST_MAPPING
@@ -1,13 +1,13 @@
{
"presubmit": [
{
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.PermissionPolicyTest#platformPermissionPolicyIsUnaltered"
+ "include-filter": "android.permissionpolicy.cts.PermissionPolicyTest#platformPermissionPolicyIsUnaltered"
},
{
- "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ "include-filter": "android.permissionpolicy.cts.RuntimePermissionProperties"
}
]
}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index e811bb6..3ea1592 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -20,6 +20,7 @@
"BinderProxyCountingTestService/src/**/*.java",
"BinderDeathRecipientHelperApp/src/**/*.java",
"aidl/**/I*.aidl",
+ ":FrameworksCoreTestDoubles-sources",
],
aidl: {
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java
new file mode 100644
index 0000000..4e59064
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.SurfaceTexture;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.DisplayMetrics;
+import android.view.ContentRecordingSession;
+import android.view.Surface;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+/**
+ * Tests for non-public APIs in {@link VirtualDisplayConfig}.
+ * See also related CTS tests.
+ *
+ * Run with:
+ * atest FrameworksCoreTests:VirtualDisplayConfigTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VirtualDisplayConfigTest {
+
+ private static final String NAME = "VirtualDisplayConfigTest";
+ private static final int WIDTH = 720;
+ private static final int HEIGHT = 480;
+ private static final int DENSITY = DisplayMetrics.DENSITY_MEDIUM;
+ private static final float REQUESTED_REFRESH_RATE = 30.0f;
+ private static final int FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+
+ // Values for hidden APIs.
+ private static final int DISPLAY_ID_TO_MIRROR = 10;
+ private static final IBinder WINDOW_TOKEN = new Binder("DisplayContentWindowToken");
+ private static final ContentRecordingSession CONTENT_RECORDING_SESSION =
+ ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
+
+ private final Surface mSurface = new Surface(new SurfaceTexture(/*texName=*/1));
+
+ @Test
+ public void testParcelAndUnparcel_matches() {
+ final VirtualDisplayConfig originalConfig = buildGenericVirtualDisplay(NAME);
+
+ validateConstantFields(originalConfig);
+ assertThat(originalConfig.getName()).isEqualTo(NAME);
+
+
+ final Parcel parcel = Parcel.obtain();
+ originalConfig.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+ final VirtualDisplayConfig recreatedConfig =
+ VirtualDisplayConfig.CREATOR.createFromParcel(parcel);
+
+ validateConstantFields(recreatedConfig);
+ assertThat(recreatedConfig.getName()).isEqualTo(NAME);
+ }
+
+ @Test
+ public void testEquals_matches() {
+ assertThat(buildGenericVirtualDisplay(NAME)).isEqualTo(buildGenericVirtualDisplay(NAME));
+ }
+
+ @Test
+ public void testEquals_different() {
+ assertThat(buildGenericVirtualDisplay(NAME + "2")).isNotEqualTo(
+ buildGenericVirtualDisplay(NAME));
+ }
+
+ private VirtualDisplayConfig buildGenericVirtualDisplay(String name) {
+ return new VirtualDisplayConfig.Builder(name, WIDTH, HEIGHT, DENSITY)
+ .setFlags(FLAGS)
+ .setSurface(mSurface)
+ .setDisplayCategories(Set.of("C1", "C2"))
+ .addDisplayCategory("C3")
+ .setRequestedRefreshRate(REQUESTED_REFRESH_RATE)
+ .setDisplayIdToMirror(DISPLAY_ID_TO_MIRROR)
+ .setWindowManagerMirroringEnabled(true)
+ .setContentRecordingSession(CONTENT_RECORDING_SESSION)
+ .build();
+ }
+
+ private void validateConstantFields(VirtualDisplayConfig config) {
+ assertThat(config.getWidth()).isEqualTo(WIDTH);
+ assertThat(config.getHeight()).isEqualTo(HEIGHT);
+ assertThat(config.getDensityDpi()).isEqualTo(DENSITY);
+ assertThat(config.getFlags()).isEqualTo(FLAGS);
+ assertThat(config.getSurface()).isNotNull();
+ assertThat(config.getDisplayCategories()).containsExactly("C1", "C2", "C3");
+ assertThat(config.getRequestedRefreshRate()).isEqualTo(REQUESTED_REFRESH_RATE);
+ assertThat(config.getDisplayIdToMirror()).isEqualTo(DISPLAY_ID_TO_MIRROR);
+ assertThat(config.isWindowManagerMirroringEnabled()).isTrue();
+ assertThat(config.getContentRecordingSession()).isEqualTo(CONTENT_RECORDING_SESSION);
+ }
+}
diff --git a/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java b/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
index 4eea076..7d5a0364 100644
--- a/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
+++ b/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
@@ -59,7 +59,10 @@
.thenReturn(175L)
.thenReturn(198L)
.thenReturn(203L)
- .thenReturn(209L);
+ .thenReturn(209L)
+ .thenReturn(211L)
+ .thenReturn(212L)
+ .thenReturn(220L);
}
@Test
@@ -68,6 +71,7 @@
mLatencyTracker.appNotRespondingStarted();
mLatencyTracker.waitingOnAnrRecordLockStarted();
mLatencyTracker.waitingOnAnrRecordLockEnded();
+ mLatencyTracker.earlyDumpRequestSubmittedWithSize(5);
mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
mLatencyTracker.appNotRespondingEnded();
@@ -90,7 +94,16 @@
mLatencyTracker.waitingOnProcLockStarted();
mLatencyTracker.waitingOnProcLockEnded();
+ mLatencyTracker.dumpStackTracesTempFileStarted();
+ mLatencyTracker.dumpingPidStarted(5);
+
mLatencyTracker.dumpStackTracesStarted();
+ mLatencyTracker.copyingFirstPidStarted();
+
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpStackTracesTempFileEnded();
+
+ mLatencyTracker.copyingFirstPidEnded(true);
mLatencyTracker.dumpingFirstPidsStarted();
mLatencyTracker.dumpingPidStarted(1);
mLatencyTracker.dumpingPidEnded();
@@ -111,7 +124,7 @@
mLatencyTracker.close();
assertThat(mLatencyTracker.dumpAsCommaSeparatedArrayWithHeader())
- .isEqualTo("DurationsV2: 50,5,25,8,115,2,3,7,8,15,2,7,23,10,3,6\n\n");
+ .isEqualTo("DurationsV3: 50,5,33,11,112,4,2,4,6,5,1,10,5,10,3,9,11,129,5,8,1\n\n");
verify(mLatencyTracker, times(1)).pushAtom();
}
@@ -121,6 +134,7 @@
mLatencyTracker.appNotRespondingStarted();
mLatencyTracker.waitingOnAnrRecordLockStarted();
mLatencyTracker.waitingOnAnrRecordLockEnded();
+ mLatencyTracker.earlyDumpRequestSubmittedWithSize(5);
mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
mLatencyTracker.appNotRespondingEnded();
@@ -143,7 +157,18 @@
mLatencyTracker.waitingOnProcLockStarted();
mLatencyTracker.waitingOnProcLockEnded();
+
+
+ mLatencyTracker.dumpStackTracesTempFileStarted();
+ mLatencyTracker.dumpingPidStarted(5);
+
mLatencyTracker.dumpStackTracesStarted();
+ mLatencyTracker.copyingFirstPidStarted();
+
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpStackTracesTempFileEnded();
+
+ mLatencyTracker.copyingFirstPidEnded(true);
mLatencyTracker.dumpingFirstPidsStarted();
mLatencyTracker.dumpingPidStarted(1);
mLatencyTracker.dumpingPidEnded();
diff --git a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
new file mode 100644
index 0000000..e6f10ad
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.internal.util;
+
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
+import static com.android.internal.util.FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED;
+import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * This test class verifies the additional methods which {@link FakeLatencyTracker} exposes.
+ *
+ * <p>The typical {@link LatencyTracker} behavior test coverage is present in
+ * {@link LatencyTrackerTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class FakeLatencyTrackerTest {
+
+ private FakeLatencyTracker mFakeLatencyTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ mFakeLatencyTracker = FakeLatencyTracker.create();
+ }
+
+ @Test
+ public void testForceEnabled() throws Exception {
+ mFakeLatencyTracker.logAction(ACTION_SHOW_VOICE_INTERACTION, 1234);
+
+ assertThat(mFakeLatencyTracker.getEventsWrittenToFrameworkStats(
+ ACTION_SHOW_VOICE_INTERACTION)).isEmpty();
+
+ mFakeLatencyTracker.forceEnabled(ACTION_SHOW_VOICE_INTERACTION, 1000);
+ mFakeLatencyTracker.logAction(ACTION_SHOW_VOICE_INTERACTION, 1234);
+ List<LatencyTracker.FrameworkStatsLogEvent> events =
+ mFakeLatencyTracker.getEventsWrittenToFrameworkStats(
+ ACTION_SHOW_VOICE_INTERACTION);
+ assertThat(events).hasSize(1);
+ assertThat(events.get(0).logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
+ assertThat(events.get(0).statsdAction).isEqualTo(
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION);
+ assertThat(events.get(0).durationMillis).isEqualTo(1234);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
index d1f0b5e..645324d 100644
--- a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -16,19 +16,22 @@
package com.android.internal.util;
+import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER;
import static android.text.TextUtils.formatSimple;
+import static com.android.internal.util.FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED;
import static com.android.internal.util.LatencyTracker.STATSD_ACTION;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.provider.DeviceConfig;
-import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.util.LatencyTracker.ActionProperties;
+
import com.google.common.truth.Expect;
import org.junit.Before;
@@ -38,7 +41,6 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
-import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -49,27 +51,23 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LatencyTrackerTest {
- private static final String TAG = LatencyTrackerTest.class.getSimpleName();
private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__";
- private static final String ACTION_ENABLE_SUFFIX = "_enable";
- private static final Duration TEST_TIMEOUT = Duration.ofMillis(500);
@Rule
public final Expect mExpect = Expect.create();
+ // Fake is used because it tests the real logic of LatencyTracker, and it only fakes the
+ // outcomes (PerfettoTrigger and FrameworkStatsLog).
+ private FakeLatencyTracker mLatencyTracker;
+
@Before
- public void setUp() {
- DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
- LatencyTracker.SETTINGS_ENABLED_KEY);
- getAllActions().forEach(action -> {
- DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
- action.getName().toLowerCase() + ACTION_ENABLE_SUFFIX);
- });
+ public void setUp() throws Exception {
+ mLatencyTracker = FakeLatencyTracker.create();
}
@Test
public void testCujsMapToEnumsCorrectly() {
- List<Field> actions = getAllActions();
+ List<Field> actions = getAllActionFields();
Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
.filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
&& Modifier.isStatic(f.getModifiers())
@@ -101,7 +99,7 @@
@Test
public void testCujTypeEnumCorrectlyDefined() throws Exception {
- List<Field> cujEnumFields = getAllActions();
+ List<Field> cujEnumFields = getAllActionFields();
HashSet<Integer> allValues = new HashSet<>();
for (Field field : cujEnumFields) {
int fieldValue = field.getInt(null);
@@ -118,92 +116,242 @@
}
@Test
- public void testIsEnabled_globalEnabled() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ public void testIsEnabled_trueWhenGlobalEnabled() throws Exception {
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
- LatencyTracker latencyTracker = new LatencyTracker();
- waitForLatencyTrackerToUpdateProperties(latencyTracker);
- assertThat(latencyTracker.isEnabled()).isTrue();
+ mLatencyTracker.waitForGlobalEnabledState(true);
+ mLatencyTracker.waitForAllPropertiesEnableState(true);
+
+ //noinspection deprecation
+ assertThat(mLatencyTracker.isEnabled()).isTrue();
}
@Test
- public void testIsEnabled_globalDisabled() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ public void testIsEnabled_falseWhenGlobalDisabled() throws Exception {
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
- LatencyTracker latencyTracker = new LatencyTracker();
- waitForLatencyTrackerToUpdateProperties(latencyTracker);
- assertThat(latencyTracker.isEnabled()).isFalse();
+ mLatencyTracker.waitForGlobalEnabledState(false);
+ mLatencyTracker.waitForAllPropertiesEnableState(false);
+
+ //noinspection deprecation
+ assertThat(mLatencyTracker.isEnabled()).isFalse();
}
@Test
- public void testIsEnabledAction_useGlobalValueWhenActionEnableIsNotSet() {
- LatencyTracker latencyTracker = new LatencyTracker();
+ public void testIsEnabledAction_useGlobalValueWhenActionEnableIsNotSet()
+ throws Exception {
// using a single test action, but this applies to all actions
int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
- Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY + ", value=true");
- latencyTracker.mDeviceConfigPropertiesUpdated.close();
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ DeviceConfig.deleteProperty(NAMESPACE_LATENCY_TRACKER,
+ "action_show_voice_interaction_enable");
+ mLatencyTracker.waitForAllPropertiesEnableState(false);
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
LatencyTracker.SETTINGS_ENABLED_KEY, "true", false);
- waitForLatencyTrackerToUpdateProperties(latencyTracker);
- assertThat(
- latencyTracker.isEnabled(action)).isTrue();
+ mLatencyTracker.waitForGlobalEnabledState(true);
+ mLatencyTracker.waitForAllPropertiesEnableState(true);
- Log.i(TAG, "setting property=" + LatencyTracker.SETTINGS_ENABLED_KEY
- + ", value=false");
- latencyTracker.mDeviceConfigPropertiesUpdated.close();
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
- LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
- waitForLatencyTrackerToUpdateProperties(latencyTracker);
- assertThat(latencyTracker.isEnabled(action)).isFalse();
+ assertThat(mLatencyTracker.isEnabled(action)).isTrue();
}
@Test
public void testIsEnabledAction_actionPropertyOverridesGlobalProperty()
- throws DeviceConfig.BadConfigException {
- LatencyTracker latencyTracker = new LatencyTracker();
+ throws Exception {
// using a single test action, but this applies to all actions
int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
- String actionEnableProperty = "action_show_voice_interaction" + ACTION_ENABLE_SUFFIX;
- Log.i(TAG, "setting property=" + actionEnableProperty + ", value=true");
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER,
+ LatencyTracker.SETTINGS_ENABLED_KEY, "false", false);
+ mLatencyTracker.waitForGlobalEnabledState(false);
- latencyTracker.mDeviceConfigPropertiesUpdated.close();
- Map<String, String> properties = new HashMap<String, String>() {{
- put(LatencyTracker.SETTINGS_ENABLED_KEY, "false");
- put(actionEnableProperty, "true");
- }};
+ Map<String, String> deviceConfigProperties = new HashMap<>();
+ deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+ deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+ deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "-1");
DeviceConfig.setProperties(
- new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
- properties));
- waitForLatencyTrackerToUpdateProperties(latencyTracker);
- assertThat(latencyTracker.isEnabled(action)).isTrue();
+ new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+ deviceConfigProperties));
- latencyTracker.mDeviceConfigPropertiesUpdated.close();
- Log.i(TAG, "setting property=" + actionEnableProperty + ", value=false");
- properties.put(LatencyTracker.SETTINGS_ENABLED_KEY, "true");
- properties.put(actionEnableProperty, "false");
- DeviceConfig.setProperties(
- new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
- properties));
- waitForLatencyTrackerToUpdateProperties(latencyTracker);
- assertThat(latencyTracker.isEnabled(action)).isFalse();
+ mLatencyTracker.waitForMatchingActionProperties(
+ new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+ -1 /* traceThreshold */));
+
+ assertThat(mLatencyTracker.isEnabled(action)).isTrue();
}
- private void waitForLatencyTrackerToUpdateProperties(LatencyTracker latencyTracker) {
- try {
- Thread.sleep(TEST_TIMEOUT.toMillis());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- assertThat(latencyTracker.mDeviceConfigPropertiesUpdated.block(
- TEST_TIMEOUT.toMillis())).isTrue();
+ @Test
+ public void testLogsWhenEnabled() throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ Map<String, String> deviceConfigProperties = new HashMap<>();
+ deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+ deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+ deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "-1");
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+ deviceConfigProperties));
+ mLatencyTracker.waitForMatchingActionProperties(
+ new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+ -1 /* traceThreshold */));
+
+ mLatencyTracker.logAction(action, 1234);
+ assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).hasSize(1);
+ LatencyTracker.FrameworkStatsLogEvent frameworkStatsLog =
+ mLatencyTracker.getEventsWrittenToFrameworkStats(action).get(0);
+ assertThat(frameworkStatsLog.logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
+ assertThat(frameworkStatsLog.statsdAction).isEqualTo(STATSD_ACTION[action]);
+ assertThat(frameworkStatsLog.durationMillis).isEqualTo(1234);
+
+ mLatencyTracker.clearEvents();
+
+ mLatencyTracker.onActionStart(action);
+ mLatencyTracker.onActionEnd(action);
+ // assert that action was logged, but we cannot confirm duration logged
+ assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).hasSize(1);
+ frameworkStatsLog = mLatencyTracker.getEventsWrittenToFrameworkStats(action).get(0);
+ assertThat(frameworkStatsLog.logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
+ assertThat(frameworkStatsLog.statsdAction).isEqualTo(STATSD_ACTION[action]);
}
- private List<Field> getAllActions() {
- return Arrays.stream(LatencyTracker.class.getDeclaredFields())
- .filter(field -> field.getName().startsWith("ACTION_")
- && Modifier.isStatic(field.getModifiers())
- && field.getType() == int.class)
- .collect(Collectors.toList());
+ @Test
+ public void testDoesNotLogWhenDisabled() throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER, "action_show_voice_interaction_enable",
+ "false", false);
+ mLatencyTracker.waitForActionEnabledState(action, false);
+ assertThat(mLatencyTracker.isEnabled(action)).isFalse();
+
+ mLatencyTracker.logAction(action, 1234);
+ assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+
+ mLatencyTracker.onActionStart(action);
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+ }
+
+ @Test
+ public void testOnActionEndDoesNotLogWithoutOnActionStart()
+ throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER, "action_show_voice_interaction_enable",
+ "true", false);
+ mLatencyTracker.waitForActionEnabledState(action, true);
+ assertThat(mLatencyTracker.isEnabled(action)).isTrue();
+
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+ }
+
+ @Test
+ public void testOnActionEndDoesNotLogWhenCanceled()
+ throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ DeviceConfig.setProperty(NAMESPACE_LATENCY_TRACKER, "action_show_voice_interaction_enable",
+ "true", false);
+ mLatencyTracker.waitForActionEnabledState(action, true);
+ assertThat(mLatencyTracker.isEnabled(action)).isTrue();
+
+ mLatencyTracker.onActionStart(action);
+ mLatencyTracker.onActionCancel(action);
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getEventsWrittenToFrameworkStats(action)).isEmpty();
+ }
+
+ @Test
+ public void testNeverTriggersPerfettoWhenThresholdNegative()
+ throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ Map<String, String> deviceConfigProperties = new HashMap<>();
+ deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+ deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+ deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "-1");
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+ deviceConfigProperties));
+ mLatencyTracker.waitForMatchingActionProperties(
+ new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+ -1 /* traceThreshold */));
+
+ mLatencyTracker.onActionStart(action);
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).isEmpty();
+ }
+
+ @Test
+ public void testNeverTriggersPerfettoWhenDisabled()
+ throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ Map<String, String> deviceConfigProperties = new HashMap<>();
+ deviceConfigProperties.put("action_show_voice_interaction_enable", "false");
+ deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+ deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "1");
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+ deviceConfigProperties));
+ mLatencyTracker.waitForMatchingActionProperties(
+ new ActionProperties(action, false /* enabled */, 1 /* samplingInterval */,
+ 1 /* traceThreshold */));
+
+ mLatencyTracker.onActionStart(action);
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).isEmpty();
+ }
+
+ @Test
+ public void testTriggersPerfettoWhenAboveThreshold()
+ throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ Map<String, String> deviceConfigProperties = new HashMap<>();
+ deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+ deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+ deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "1");
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+ deviceConfigProperties));
+ mLatencyTracker.waitForMatchingActionProperties(
+ new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+ 1 /* traceThreshold */));
+
+ mLatencyTracker.onActionStart(action);
+ // We need to sleep here to ensure that the end call is past the set trace threshold (1ms)
+ Thread.sleep(5 /* millis */);
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).hasSize(1);
+ assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames().get(0)).isEqualTo(
+ "com.android.telemetry.latency-tracker-ACTION_SHOW_VOICE_INTERACTION");
+ }
+
+ @Test
+ public void testNeverTriggersPerfettoWhenBelowThreshold()
+ throws Exception {
+ // using a single test action, but this applies to all actions
+ int action = LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
+ Map<String, String> deviceConfigProperties = new HashMap<>();
+ deviceConfigProperties.put("action_show_voice_interaction_enable", "true");
+ deviceConfigProperties.put("action_show_voice_interaction_sample_interval", "1");
+ deviceConfigProperties.put("action_show_voice_interaction_trace_threshold", "1000");
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(NAMESPACE_LATENCY_TRACKER,
+ deviceConfigProperties));
+ mLatencyTracker.waitForMatchingActionProperties(
+ new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+ 1000 /* traceThreshold */));
+
+ mLatencyTracker.onActionStart(action);
+ // No sleep here to ensure that end call comes before 1000ms threshold
+ mLatencyTracker.onActionEnd(action);
+ assertThat(mLatencyTracker.getTriggeredPerfettoTraceNames()).isEmpty();
+ }
+
+ private List<Field> getAllActionFields() {
+ return Arrays.stream(LatencyTracker.class.getDeclaredFields()).filter(
+ field -> field.getName().startsWith("ACTION_") && Modifier.isStatic(
+ field.getModifiers()) && field.getType() == int.class).collect(
+ Collectors.toList());
}
private int getIntFieldChecked(Field field) {
diff --git a/core/tests/coretests/src/com/android/internal/util/OWNERS b/core/tests/coretests/src/com/android/internal/util/OWNERS
index d832745..dda11fb 100644
--- a/core/tests/coretests/src/com/android/internal/util/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/util/OWNERS
@@ -1,2 +1,3 @@
per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
-per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
\ No newline at end of file
+per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
diff --git a/core/tests/coretests/testdoubles/Android.bp b/core/tests/coretests/testdoubles/Android.bp
new file mode 100644
index 0000000..35f6911
--- /dev/null
+++ b/core/tests/coretests/testdoubles/Android.bp
@@ -0,0 +1,19 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-BSD
+ // legacy_unencumbered
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "FrameworksCoreTestDoubles-sources",
+ srcs: ["src/**/*.java"],
+ visibility: [
+ "//frameworks/base/core/tests/coretests",
+ "//frameworks/base/services/tests/voiceinteractiontests",
+ ],
+}
diff --git a/core/tests/coretests/testdoubles/OWNERS b/core/tests/coretests/testdoubles/OWNERS
new file mode 100644
index 0000000..baf92ec
--- /dev/null
+++ b/core/tests/coretests/testdoubles/OWNERS
@@ -0,0 +1 @@
+per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
diff --git a/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java b/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
new file mode 100644
index 0000000..306ecde
--- /dev/null
+++ b/core/tests/coretests/testdoubles/src/com/android/internal/util/FakeLatencyTracker.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2023 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.internal.util;
+
+import static com.android.internal.util.LatencyTracker.ActionProperties.ENABLE_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
+import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.ConditionVariable;
+import android.provider.DeviceConfig;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+public final class FakeLatencyTracker extends LatencyTracker {
+
+ private static final String TAG = "FakeLatencyTracker";
+ private static final Duration FORCE_UPDATE_TIMEOUT = Duration.ofSeconds(1);
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<Integer, List<FrameworkStatsLogEvent>> mLatenciesLogged;
+ @GuardedBy("mLock")
+ private final List<String> mPerfettoTraceNamesTriggered;
+ private final AtomicReference<SparseArray<ActionProperties>> mLastPropertiesUpdate =
+ new AtomicReference<>();
+ @Nullable
+ @GuardedBy("mLock")
+ private Callable<Boolean> mShouldClosePropertiesUpdatedCallable = null;
+ private final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();
+
+ public static FakeLatencyTracker create() throws Exception {
+ Log.i(TAG, "create");
+ disableForAllActions();
+ FakeLatencyTracker fakeLatencyTracker = new FakeLatencyTracker();
+ // always return the fake in the disabled state and let the client control the desired state
+ fakeLatencyTracker.waitForGlobalEnabledState(false);
+ fakeLatencyTracker.waitForAllPropertiesEnableState(false);
+ return fakeLatencyTracker;
+ }
+
+ FakeLatencyTracker() {
+ super();
+ mLatenciesLogged = new HashMap<>();
+ mPerfettoTraceNamesTriggered = new ArrayList<>();
+ }
+
+ private static void disableForAllActions() throws DeviceConfig.BadConfigException {
+ Map<String, String> properties = new HashMap<>();
+ properties.put(LatencyTracker.SETTINGS_ENABLED_KEY, "false");
+ for (int action : STATSD_ACTION) {
+ Log.d(TAG, "disabling action=" + action + ", property=" + getNameOfAction(
+ action).toLowerCase(Locale.ROOT) + ENABLE_SUFFIX);
+ properties.put(getNameOfAction(action).toLowerCase(Locale.ROOT) + ENABLE_SUFFIX,
+ "false");
+ }
+
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER, properties));
+ }
+
+ public void forceEnabled(int action, int traceThresholdMillis)
+ throws Exception {
+ String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
+ String actionEnableProperty = actionName + ENABLE_SUFFIX;
+ String actionSampleProperty = actionName + SAMPLE_INTERVAL_SUFFIX;
+ String actionTraceProperty = actionName + TRACE_THRESHOLD_SUFFIX;
+ Log.i(TAG, "setting property=" + actionTraceProperty + ", value=" + traceThresholdMillis);
+ Log.i(TAG, "setting property=" + actionEnableProperty + ", value=true");
+
+ Map<String, String> properties = new HashMap<>(ImmutableMap.of(
+ actionEnableProperty, "true",
+ // Fake forces to sample every event
+ actionSampleProperty, String.valueOf(1),
+ actionTraceProperty, String.valueOf(traceThresholdMillis)
+ ));
+ DeviceConfig.setProperties(
+ new DeviceConfig.Properties(DeviceConfig.NAMESPACE_LATENCY_TRACKER, properties));
+ waitForMatchingActionProperties(
+ new ActionProperties(action, true /* enabled */, 1 /* samplingInterval */,
+ traceThresholdMillis));
+ }
+
+ public List<FrameworkStatsLogEvent> getEventsWrittenToFrameworkStats(@Action int action) {
+ synchronized (mLock) {
+ Log.i(TAG, "getEventsWrittenToFrameworkStats: mLatenciesLogged=" + mLatenciesLogged);
+ return mLatenciesLogged.getOrDefault(action, Collections.emptyList());
+ }
+ }
+
+ public List<String> getTriggeredPerfettoTraceNames() {
+ synchronized (mLock) {
+ return mPerfettoTraceNamesTriggered;
+ }
+ }
+
+ public void clearEvents() {
+ synchronized (mLock) {
+ mLatenciesLogged.clear();
+ mPerfettoTraceNamesTriggered.clear();
+ }
+ }
+
+ @Override
+ public void onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties) {
+ Log.d(TAG, "onDeviceConfigPropertiesUpdated: " + actionProperties);
+ mLastPropertiesUpdate.set(actionProperties);
+ synchronized (mLock) {
+ if (mShouldClosePropertiesUpdatedCallable != null) {
+ try {
+ boolean shouldClosePropertiesUpdated =
+ mShouldClosePropertiesUpdatedCallable.call();
+ Log.i(TAG, "shouldClosePropertiesUpdatedCallable callable result="
+ + shouldClosePropertiesUpdated);
+ if (shouldClosePropertiesUpdated) {
+ Log.i(TAG, "shouldClosePropertiesUpdatedCallable=true, opening condition");
+ mShouldClosePropertiesUpdatedCallable = null;
+ mDeviceConfigPropertiesUpdated.open();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "exception when calling callable", e);
+ throw new RuntimeException(e);
+ }
+ } else {
+ Log.i(TAG, "no conditional callable set, opening condition");
+ mDeviceConfigPropertiesUpdated.open();
+ }
+ }
+ }
+
+ @Override
+ public void onTriggerPerfetto(String triggerName) {
+ synchronized (mLock) {
+ mPerfettoTraceNamesTriggered.add(triggerName);
+ }
+ }
+
+ @Override
+ public void onLogToFrameworkStats(FrameworkStatsLogEvent event) {
+ synchronized (mLock) {
+ Log.i(TAG, "onLogToFrameworkStats: event=" + event);
+ List<FrameworkStatsLogEvent> eventList = mLatenciesLogged.getOrDefault(event.action,
+ new ArrayList<>());
+ eventList.add(event);
+ mLatenciesLogged.put(event.action, eventList);
+ }
+ }
+
+ public void waitForAllPropertiesEnableState(boolean enabledState) throws Exception {
+ Log.i(TAG, "waitForAllPropertiesEnableState: enabledState=" + enabledState);
+ synchronized (mLock) {
+ Log.i(TAG, "closing condition");
+ mDeviceConfigPropertiesUpdated.close();
+ // Update the callable to only close the properties updated condition when all the
+ // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+ // times so testing the resulting updates is required.
+ mShouldClosePropertiesUpdatedCallable = () -> {
+ Log.i(TAG, "verifying if last properties update has all properties enable="
+ + enabledState);
+ SparseArray<ActionProperties> newProperties = mLastPropertiesUpdate.get();
+ if (newProperties != null) {
+ for (int i = 0; i < newProperties.size(); i++) {
+ if (newProperties.get(i).isEnabled() != enabledState) {
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+ if (mShouldClosePropertiesUpdatedCallable.call()) {
+ return;
+ }
+ }
+ Log.i(TAG, "waiting for condition");
+ assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+ }
+
+ public void waitForMatchingActionProperties(ActionProperties actionProperties)
+ throws Exception {
+ Log.i(TAG, "waitForMatchingActionProperties: actionProperties=" + actionProperties);
+ synchronized (mLock) {
+ Log.i(TAG, "closing condition");
+ mDeviceConfigPropertiesUpdated.close();
+ // Update the callable to only close the properties updated condition when all the
+ // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+ // times so testing the resulting updates is required.
+ mShouldClosePropertiesUpdatedCallable = () -> {
+ Log.i(TAG, "verifying if last properties update contains matching property ="
+ + actionProperties);
+ SparseArray<ActionProperties> newProperties = mLastPropertiesUpdate.get();
+ if (newProperties != null) {
+ if (newProperties.size() > 0) {
+ return newProperties.get(actionProperties.getAction()).equals(
+ actionProperties);
+ }
+ }
+ return false;
+ };
+ if (mShouldClosePropertiesUpdatedCallable.call()) {
+ return;
+ }
+ }
+ Log.i(TAG, "waiting for condition");
+ assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+ }
+
+ public void waitForActionEnabledState(int action, boolean enabledState) throws Exception {
+ Log.i(TAG, "waitForActionEnabledState:"
+ + " action=" + action + ", enabledState=" + enabledState);
+ synchronized (mLock) {
+ Log.i(TAG, "closing condition");
+ mDeviceConfigPropertiesUpdated.close();
+ // Update the callable to only close the properties updated condition when all the
+ // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+ // times so testing the resulting updates is required.
+ mShouldClosePropertiesUpdatedCallable = () -> {
+ Log.i(TAG, "verifying if last properties update contains action=" + action
+ + ", enabledState=" + enabledState);
+ SparseArray<ActionProperties> newProperties = mLastPropertiesUpdate.get();
+ if (newProperties != null) {
+ if (newProperties.size() > 0) {
+ return newProperties.get(action).isEnabled() == enabledState;
+ }
+ }
+ return false;
+ };
+ if (mShouldClosePropertiesUpdatedCallable.call()) {
+ return;
+ }
+ }
+ Log.i(TAG, "waiting for condition");
+ assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+ }
+
+ public void waitForGlobalEnabledState(boolean enabledState) throws Exception {
+ Log.i(TAG, "waitForGlobalEnabledState: enabledState=" + enabledState);
+ synchronized (mLock) {
+ Log.i(TAG, "closing condition");
+ mDeviceConfigPropertiesUpdated.close();
+ // Update the callable to only close the properties updated condition when all the
+ // desired properties have been updated. The DeviceConfig callbacks may happen multiple
+ // times so testing the resulting updates is required.
+ mShouldClosePropertiesUpdatedCallable = () -> {
+ //noinspection deprecation
+ return isEnabled() == enabledState;
+ };
+ if (mShouldClosePropertiesUpdatedCallable.call()) {
+ return;
+ }
+ }
+ Log.i(TAG, "waiting for condition");
+ assertThat(mDeviceConfigPropertiesUpdated.block(FORCE_UPDATE_TIMEOUT.toMillis())).isTrue();
+ }
+}
diff --git a/data/etc/TEST_MAPPING b/data/etc/TEST_MAPPING
index 1a5db2f..5927720 100644
--- a/data/etc/TEST_MAPPING
+++ b/data/etc/TEST_MAPPING
@@ -2,10 +2,10 @@
"presubmit": [
{
"file_patterns": ["(/|^)platform.xml"],
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ "include-filter": "android.permissionpolicy.cts.RuntimePermissionProperties"
}
]
}
diff --git a/libs/WindowManager/Shell/res/color/unfold_background.xml b/libs/WindowManager/Shell/res/color/unfold_background.xml
new file mode 100644
index 0000000..e33eb12
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/unfold_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="5" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
index 27e0b18..5d77713 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
@@ -14,13 +14,12 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="@color/decor_button_dark_color">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<group android:translateY="8.0">
<path
- android:fillColor="@android:color/white" android:pathData="M3,5V3H21V5Z"/>
+ android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
new file mode 100644
index 0000000..3e0297a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector android:height="24dp" android:tint="#000000"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/black" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
new file mode 100644
index 0000000..35562b6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/desktop_mode_caption"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:background="@drawable/desktop_mode_decor_title">
+
+ <LinearLayout
+ android:id="@+id/open_menu_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clickable="true"
+ android:focusable="true"
+ android:paddingStart="8dp"
+ android:background="?android:selectableItemBackgroundBorderless">
+
+ <ImageView
+ android:id="@+id/application_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_margin="4dp"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/app_icon_text" />
+
+ <TextView
+ android:id="@+id/application_name"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:minWidth="80dp"
+ android:textColor="@color/desktop_mode_caption_app_name_dark"
+ android:textSize="14sp"
+ android:textFontWeight="500"
+ android:gravity="center_vertical"
+ android:layout_weight="1"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ tools:text="Gmail"/>
+
+ <ImageButton
+ android:id="@+id/expand_menu_button"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:padding="4dp"
+ android:contentDescription="@string/collapse_menu_text"
+ android:src="@drawable/ic_baseline_expand_more_24"
+ android:tint="@color/desktop_mode_caption_expand_button_dark"
+ android:background="@null"
+ android:scaleType="fitCenter"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/caption_handle"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/close_window"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="4dp"
+ android:layout_marginEnd="8dp"
+ android:contentDescription="@string/close_button_text"
+ android:src="@drawable/decor_close_button_dark"
+ android:scaleType="fitCenter"
+ android:gravity="end"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:tint="@color/desktop_mode_caption_close_button_dark"/>
+</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
index f9aeb6a..ac13eae 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
@@ -50,7 +50,7 @@
android:layout_marginEnd="10dp"
android:contentDescription="@string/collapse_menu_text"
android:layout_alignParentEnd="true"
- android:background="@drawable/caption_collapse_menu_button"
+ android:background="@drawable/ic_baseline_expand_more_24"
android:layout_centerVertical="true"/>
</RelativeLayout>
<LinearLayout
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
similarity index 67%
rename from libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
rename to libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index 29cf151..5ab159c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -16,26 +16,21 @@
-->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/desktop_mode_caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="@drawable/desktop_mode_decor_title">
- <Button
- style="@style/CaptionButtonStyle"
- android:id="@+id/back_button"
- android:contentDescription="@string/back_button_text"
- android:background="@drawable/decor_back_button_dark"/>
- <Button
+
+ <ImageButton
android:id="@+id/caption_handle"
android:layout_width="128dp"
- android:layout_height="32dp"
- android:layout_margin="5dp"
+ android:layout_height="42dp"
android:contentDescription="@string/handle_text"
- android:background="@drawable/decor_handle_dark"/>
- <Button
- style="@style/CaptionButtonStyle"
- android:id="@+id/close_window"
- android:contentDescription="@string/close_button_text"
- android:background="@drawable/decor_close_button_dark"/>
+ android:src="@drawable/decor_handle_dark"
+ tools:tint="@color/desktop_mode_caption_handle_bar_dark"
+ android:scaleType="fitXY"
+ android:background="?android:selectableItemBackground"/>
+
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 6fb70006..2a03b03 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -54,4 +54,14 @@
<color name="splash_screen_bg_light">#FFFFFF</color>
<color name="splash_screen_bg_dark">#000000</color>
<color name="splash_window_background_default">@color/splash_screen_bg_light</color>
+
+ <!-- Desktop Mode -->
+ <color name="desktop_mode_caption_handle_bar_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_handle_bar_dark">#1C1C17</color>
+ <color name="desktop_mode_caption_expand_button_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_expand_button_dark">#48473A</color>
+ <color name="desktop_mode_caption_close_button_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_close_button_dark">#1C1C17</color>
+ <color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e24c228..85a353f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -206,12 +206,14 @@
public Bubble(Intent intent,
UserHandle user,
+ @Nullable Icon icon,
Executor mainExecutor) {
mKey = KEY_APP_BUBBLE;
mGroupKey = null;
mLocusId = null;
mFlags = 0;
mUser = user;
+ mIcon = icon;
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
mTaskId = INVALID_TASK_ID;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 48fe65d..d2889e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -57,6 +57,7 @@
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.drawable.Icon;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
@@ -1034,8 +1035,9 @@
*
* @param intent the intent to display in the bubble expanded view.
* @param user the {@link UserHandle} of the user to start this activity for.
+ * @param icon the {@link Icon} to use for the bubble view.
*/
- public void showOrHideAppBubble(Intent intent, UserHandle user) {
+ public void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon) {
if (intent == null || intent.getPackage() == null) {
Log.w(TAG, "App bubble failed to show, invalid intent: " + intent
+ ((intent != null) ? " with package: " + intent.getPackage() : " "));
@@ -1063,7 +1065,7 @@
}
} else {
// App bubble does not exist, lets add and expand it
- Bubble b = new Bubble(intent, user, mMainExecutor);
+ Bubble b = new Bubble(intent, user, icon, mMainExecutor);
b.setShouldAutoExpand(true);
inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
}
@@ -1871,9 +1873,9 @@
}
@Override
- public void showOrHideAppBubble(Intent intent, UserHandle user) {
+ public void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon) {
mMainExecutor.execute(
- () -> BubbleController.this.showOrHideAppBubble(intent, user));
+ () -> BubbleController.this.showOrHideAppBubble(intent, user, icon));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 5555bec..876a720 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -26,6 +26,7 @@
import android.app.NotificationChannel;
import android.content.Intent;
import android.content.pm.UserInfo;
+import android.graphics.drawable.Icon;
import android.hardware.HardwareBuffer;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
@@ -135,8 +136,9 @@
*
* @param intent the intent to display in the bubble expanded view.
* @param user the {@link UserHandle} of the user to start this activity for.
+ * @param icon the {@link Icon} to use for the bubble view.
*/
- void showOrHideAppBubble(Intent intent, UserHandle user);
+ void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon);
/** @return true if the specified {@code taskId} corresponds to app bubble's taskId. */
boolean isAppBubbleTaskId(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 52f5a8c..c19d543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -536,6 +537,15 @@
mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
return;
}
+ if (mSplitScreenOptional.isPresent()) {
+ // If pip activity will reparent to origin task case and if the origin task still under
+ // split root, just exit split screen here to ensure it could expand to fullscreen.
+ SplitScreenController split = mSplitScreenOptional.get();
+ if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) {
+ split.exitSplitScreen(INVALID_TASK_ID,
+ SplitScreenController.EXIT_REASON_APP_FINISHED);
+ }
+ }
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
// Make sure to grab the latest source hint rect as it could have been
@@ -1479,9 +1489,13 @@
applyFinishBoundsResize(wct, direction, false);
}
} else {
- final boolean isPipTopLeft =
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && isPipToTopLeft();
- applyFinishBoundsResize(wct, direction, isPipTopLeft);
+ applyFinishBoundsResize(wct, direction, isPipToTopLeft());
+ // Use sync transaction to apply finish transaction for enter split case.
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ mSyncTransactionQueue.runInSync(t -> {
+ t.merge(tx);
+ });
+ }
}
finishResizeForMenu(destinationBounds);
@@ -1518,7 +1532,10 @@
mSurfaceTransactionHelper.round(tx, mLeash, isInPip());
wct.setBounds(mToken, taskBounds);
- wct.setBoundsChangeTransaction(mToken, tx);
+ // Pip to split should use sync transaction to sync split bounds change.
+ if (direction != TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ wct.setBoundsChangeTransaction(mToken, tx);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 979b7c7..167c032 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -282,7 +282,8 @@
public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
- && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
+ && mSplitScreenControllerOptional.get().isTaskInSplitScreenForeground(
+ taskInfo.taskId);
mFocusedTaskAllowSplitScreen = isSplitScreen
|| (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
&& taskInfo.supportsMultiWindow
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index da8c805..2ec64b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -237,12 +237,14 @@
}
void cancel(boolean toHome) {
- if (mFinishCB != null && mListener != null) {
+ if (mListener != null) {
try {
mListener.onAnimationCanceled(null, null);
} catch (RemoteException e) {
Slog.e(TAG, "Error canceling recents animation", e);
}
+ }
+ if (mFinishCB != null) {
finish(toHome, false /* userLeave */);
} else {
cleanUp();
@@ -324,10 +326,9 @@
mRecentsTaskId = taskInfo.taskId;
}
}
- if (mRecentsTask == null || !hasPausingTasks) {
+ if (mRecentsTask == null && !hasPausingTasks) {
// Recents is already running apparently, so this is a no-op.
- Slog.e(TAG, "Tried to start recents while it is already running. recents="
- + mRecentsTask);
+ Slog.e(TAG, "Tried to start recents while it is already running.");
cleanUp();
return false;
}
@@ -418,7 +419,7 @@
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
final boolean isLeafTask = leafTaskFilter.test(change);
if (TransitionUtil.isOpeningType(change.getMode())) {
- if (mRecentsTask.equals(change.getContainer())) {
+ if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) {
recentsOpening = change;
} else if (isLeafTask) {
if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
@@ -431,7 +432,7 @@
openingTasks.add(change);
}
} else if (TransitionUtil.isClosingType(change.getMode())) {
- if (mRecentsTask.equals(change.getContainer())) {
+ if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) {
foundRecentsClosing = true;
} else if (isLeafTask) {
if (closingTasks == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 94b9e90..7d5ab84 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -31,7 +31,6 @@
import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -89,7 +88,6 @@
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -329,9 +327,14 @@
return mTaskOrganizer.getRunningTaskInfo(taskId);
}
+ /** Check task is under split or not by taskId. */
public boolean isTaskInSplitScreen(int taskId) {
- return isSplitScreenVisible()
- && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+ return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+ }
+
+ /** Check split is foreground and task is under split or not by taskId. */
+ public boolean isTaskInSplitScreenForeground(int taskId) {
+ return isTaskInSplitScreen(taskId) && isSplitScreenVisible();
}
public @SplitPosition int getSplitPosition(int taskId) {
@@ -339,8 +342,7 @@
}
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
- return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
- new WindowContainerTransaction());
+ return moveToStage(taskId, sideStagePosition, new WindowContainerTransaction());
}
/**
@@ -351,13 +353,13 @@
mStageCoordinator.updateSurfaces(transaction);
}
- private boolean moveToStage(int taskId, @StageType int stageType,
- @SplitPosition int stagePosition, WindowContainerTransaction wct) {
+ private boolean moveToStage(int taskId, @SplitPosition int stagePosition,
+ WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
}
- return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct);
+ return mStageCoordinator.moveToStage(task, stagePosition, wct);
}
public boolean removeFromSideStage(int taskId) {
@@ -382,10 +384,9 @@
}
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
- final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
final int stagePosition =
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
- moveToStage(taskId, stageType, stagePosition, wct);
+ moveToStage(taskId, stagePosition, wct);
}
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a1eaf85..def945e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -373,56 +373,43 @@
return STAGE_TYPE_UNDEFINED;
}
- boolean moveToStage(ActivityManager.RunningTaskInfo task, @StageType int stageType,
- @SplitPosition int stagePosition, WindowContainerTransaction wct) {
+ boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
+ WindowContainerTransaction wct) {
StageTaskListener targetStage;
int sideStagePosition;
- if (stageType == STAGE_TYPE_MAIN) {
- targetStage = mMainStage;
- sideStagePosition = reverseSplitPosition(stagePosition);
- } else if (stageType == STAGE_TYPE_SIDE) {
+ if (isSplitScreenVisible()) {
+ // If the split screen is foreground, retrieves target stage based on position.
+ targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
+ sideStagePosition = mSideStagePosition;
+ } else {
targetStage = mSideStage;
sideStagePosition = stagePosition;
- } else {
- if (isSplitScreenVisible()) {
- // If the split screen is activated, retrieves target stage based on position.
- targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
- sideStagePosition = mSideStagePosition;
- } else {
- // Exit split if it running background.
- exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
-
- targetStage = mSideStage;
- sideStagePosition = stagePosition;
- }
}
if (!isSplitActive()) {
- // prevent the fling divider to center transitioni if split screen didn't active.
- mIsDropEntering = true;
+ mSplitLayout.init();
+ prepareEnterSplitScreen(wct, task, stagePosition);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ });
+ } else {
+ setSideStagePosition(sideStagePosition, wct);
+ targetStage.addTask(task, wct);
+ targetStage.evictAllChildren(wct);
+ if (!isSplitScreenVisible()) {
+ final StageTaskListener anotherStage = targetStage == mMainStage
+ ? mSideStage : mMainStage;
+ anotherStage.reparentTopTask(wct);
+ anotherStage.evictAllChildren(wct);
+ wct.reorder(mRootTaskInfo.token, true);
+ }
+ setRootForceTranslucent(false, wct);
+ mSyncQueue.queue(wct);
}
- setSideStagePosition(sideStagePosition, wct);
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- targetStage.evictAllChildren(evictWct);
-
- // Apply surface bounds before animation start.
- SurfaceControl.Transaction startT = mTransactionPool.acquire();
- if (startT != null) {
- updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
- startT.apply();
- mTransactionPool.release(startT);
- }
- // reparent the task to an invisible split root will make the activity invisible. Reorder
- // the root task to front to make the entering transition from pip to split smooth.
- wct.reorder(mRootTaskInfo.token, true);
- wct.reorder(targetStage.mRootTaskInfo.token, true);
- targetStage.addTask(task, wct);
-
- if (!evictWct.isEmpty()) {
- wct.merge(evictWct, true /* transfer */);
- }
- mTaskOrganizer.applyTransaction(wct);
+ // Due to drag already pip task entering split by this method so need to reset flag here.
+ mIsDropEntering = false;
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index aa1e6ed..586cab0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -303,13 +303,18 @@
info.getChanges().remove(i);
}
}
+ Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+ --mixed.mInFlightSubAnimations;
+ mixed.joinFinishArgs(wct, wctCB);
+ if (mixed.mInFlightSubAnimations > 0) return;
+ mActiveTransitions.remove(mixed);
+ finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
+ };
if (pipChange == null) {
if (mixed.mLeftoversHandler != null) {
+ mixed.mInFlightSubAnimations = 1;
if (mixed.mLeftoversHandler.startAnimation(mixed.mTransition,
- info, startTransaction, finishTransaction, (wct, wctCB) -> {
- mActiveTransitions.remove(mixed);
- finishCallback.onTransitionFinished(wct, wctCB);
- })) {
+ info, startTransaction, finishTransaction, finishCB)) {
return true;
}
}
@@ -318,13 +323,6 @@
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
+ " animation because remote-animation likely doesn't support it");
- Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
- --mixed.mInFlightSubAnimations;
- mixed.joinFinishArgs(wct, wctCB);
- if (mixed.mInFlightSubAnimations > 0) return;
- mActiveTransitions.remove(mixed);
- finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
- };
// Split the transition into 2 parts: the pip part and the rest.
mixed.mInFlightSubAnimations = 2;
// make a new startTransaction because pip's startEnterAnimation "consumes" it so
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 039bde9..b8b6d5b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -534,7 +534,7 @@
}
if (info.getType() == TRANSIT_SLEEP) {
- if (activeIdx > 0) {
+ if (activeIdx > 0 || !mActiveTransitions.isEmpty() || mReadyTransitions.size() > 1) {
// Sleep starts a process of forcing all prior transitions to finish immediately
finishForSleep(null /* forceFinish */);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
index 86ca292..fe0a3fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
@@ -79,7 +79,7 @@
}
private float[] getBackgroundColor(Context context) {
- int colorInt = context.getResources().getColor(R.color.taskbar_background);
+ int colorInt = context.getResources().getColor(R.color.unfold_background);
return new float[]{
(float) red(colorInt) / 255.0F,
(float) green(colorInt) / 255.0F,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 6b45149..d2a80471 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -166,7 +166,6 @@
}
decoration.relayout(taskInfo);
- setupCaptionColor(taskInfo, decoration);
}
@Override
@@ -252,7 +251,7 @@
}
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
- } else if (id == R.id.caption_handle) {
+ } else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
decoration.createHandleMenu();
} else if (id == R.id.desktop_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
@@ -262,7 +261,6 @@
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu();
- decoration.setButtonVisibility(false);
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
}
@@ -556,13 +554,6 @@
}
}
- private void setupCaptionColor(RunningTaskInfo taskInfo,
- DesktopModeWindowDecoration decoration) {
- if (taskInfo == null || taskInfo.taskDescription == null) return;
- final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
- decoration.setCaptionColor(statusBarColor);
- }
-
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.isProto2Enabled()
@@ -602,7 +593,6 @@
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
- setupCaptionColor(taskInfo, windowDecoration);
incrementEventReceiverTasks(taskInfo.displayId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 3c0ef96..43605e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -22,15 +22,10 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.util.Log;
import android.view.Choreographer;
@@ -48,20 +43,24 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
+import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
+import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link DesktopModeWindowDecorViewModel}. The caption bar contains a handle, back button, and
- * close button.
+ * {@link DesktopModeWindowDecorViewModel}.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private static final String TAG = "DesktopModeWindowDecoration";
+
private final Handler mHandler;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
+ private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private DragPositioningCallback mDragPositioningCallback;
@@ -73,7 +72,6 @@
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
- private boolean mDesktopActive;
private AdditionalWindow mHandleMenu;
private final int mHandleMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
private final int mHandleMenuShadowRadiusId = R.dimen.caption_menu_shadow_radius;
@@ -94,7 +92,6 @@
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
- mDesktopActive = DesktopModeStatus.isActive(mContext);
}
@Override
@@ -152,9 +149,11 @@
final int outsetRightId = R.dimen.freeform_resize_handle;
final int outsetBottomId = R.dimen.freeform_resize_handle;
+ final int windowDecorLayoutId = getDesktopModeWindowDecorLayoutId(
+ taskInfo.getWindowingMode());
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
- mRelayoutParams.mLayoutResId = R.layout.desktop_mode_window_decor;
+ mRelayoutParams.mLayoutResId = windowDecorLayoutId;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
if (isDragResizeable) {
@@ -172,24 +171,28 @@
return;
}
if (oldRootView != mResult.mRootView) {
- setupRootView();
- }
-
- // If this task is not focused, do not show caption.
- setCaptionVisibility(mTaskInfo.isFocused);
-
- if (mTaskInfo.isFocused) {
- if (DesktopModeStatus.isProto2Enabled()) {
- updateButtonVisibility();
- } else if (DesktopModeStatus.isProto1Enabled()) {
- // Only handle should show if Desktop Mode is inactive.
- boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext);
- if (mDesktopActive != desktopCurrentStatus) {
- mDesktopActive = desktopCurrentStatus;
- setButtonVisibility(mDesktopActive);
- }
+ if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) {
+ mWindowDecorViewHolder = new DesktopModeFocusedWindowDecorationViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener
+ );
+ } else if (mRelayoutParams.mLayoutResId
+ == R.layout.desktop_mode_app_controls_window_decor) {
+ mWindowDecorViewHolder = new DesktopModeAppControlsWindowDecorationViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener
+ );
+ } else {
+ throw new IllegalArgumentException("Unexpected layout resource id");
}
}
+ mWindowDecorViewHolder.bindData(mTaskInfo);
+
+ if (!mTaskInfo.isFocused) {
+ closeHandleMenu();
+ }
if (!isDragResizeable) {
closeDragResizeListener();
@@ -219,24 +222,6 @@
mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
}
- /**
- * Sets up listeners when a new root view is created.
- */
- private void setupRootView() {
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- caption.setOnTouchListener(mOnCaptionTouchListener);
- final View handle = caption.findViewById(R.id.caption_handle);
- handle.setOnTouchListener(mOnCaptionTouchListener);
- handle.setOnClickListener(mOnCaptionButtonClickListener);
- if (DesktopModeStatus.isProto1Enabled()) {
- final View back = caption.findViewById(R.id.back_button);
- back.setOnClickListener(mOnCaptionButtonClickListener);
- final View close = caption.findViewById(R.id.close_window);
- close.setOnClickListener(mOnCaptionButtonClickListener);
- }
- updateButtonVisibility();
- }
-
private void setupHandleMenu() {
final View menu = mHandleMenu.mWindowViewHost.getView();
final View fullscreen = menu.findViewById(R.id.fullscreen_button);
@@ -255,98 +240,26 @@
collapse.setOnClickListener(mOnCaptionButtonClickListener);
menu.setOnTouchListener(mOnCaptionTouchListener);
- String packageName = mTaskInfo.baseActivity.getPackageName();
- PackageManager pm = mContext.getApplicationContext().getPackageManager();
- // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
- try {
- ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
- PackageManager.ApplicationInfoFlags.of(0));
- final ImageView appIcon = menu.findViewById(R.id.application_icon);
- appIcon.setImageDrawable(pm.getApplicationIcon(applicationInfo));
- final TextView appName = menu.findViewById(R.id.application_name);
- appName.setText(pm.getApplicationLabel(applicationInfo));
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Package not found: " + packageName, e);
- }
- }
-
- /**
- * Sets caption visibility based on task focus.
- * Note: Only applicable to Desktop Proto 1; Proto 2 only closes handle menu on focus loss
- * @param visible whether or not the caption should be visible
- */
- private void setCaptionVisibility(boolean visible) {
- if (!visible) closeHandleMenu();
- if (!DesktopModeStatus.isProto1Enabled()) return;
- final int v = visible ? View.VISIBLE : View.GONE;
- final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- captionView.setVisibility(v);
-
- }
-
- /**
- * Sets the visibility of buttons and color of caption based on desktop mode status
- */
- void updateButtonVisibility() {
- if (DesktopModeStatus.isProto2Enabled()) {
- setButtonVisibility(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM);
- } else if (DesktopModeStatus.isProto1Enabled()) {
- mDesktopActive = DesktopModeStatus.isActive(mContext);
- setButtonVisibility(mDesktopActive);
- }
- }
-
- /**
- * Show or hide buttons
- */
- void setButtonVisibility(boolean visible) {
- final int visibility = visible && DesktopModeStatus.isProto1Enabled()
- ? View.VISIBLE : View.GONE;
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- final View back = caption.findViewById(R.id.back_button);
- final View close = caption.findViewById(R.id.close_window);
- back.setVisibility(visibility);
- close.setVisibility(visibility);
- final int buttonTintColorRes =
- mDesktopActive ? R.color.decor_button_dark_color
- : R.color.decor_button_light_color;
- final ColorStateList buttonTintColor =
- caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
- final View handle = caption.findViewById(R.id.caption_handle);
- final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
- handleBackground.setTintList(buttonTintColor);
+ final ImageView appIcon = menu.findViewById(R.id.application_icon);
+ final TextView appName = menu.findViewById(R.id.application_name);
+ loadAppInfo(appName, appIcon);
}
boolean isHandleMenuActive() {
return mHandleMenu != null;
}
- void setCaptionColor(int captionColor) {
- if (mResult.mRootView == null) {
- return;
- }
-
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- final GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
- captionDrawable.setColor(captionColor);
-
- final int buttonTintColorRes =
- Color.valueOf(captionColor).luminance() < 0.5
- ? R.color.decor_button_light_color
- : R.color.decor_button_dark_color;
- final ColorStateList buttonTintColor =
- caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
-
- final View handle = caption.findViewById(R.id.caption_handle);
- final Drawable handleBackground = handle.getBackground();
- handleBackground.setTintList(buttonTintColor);
- if (DesktopModeStatus.isProto1Enabled()) {
- final View back = caption.findViewById(R.id.back_button);
- final Drawable backBackground = back.getBackground();
- backBackground.setTintList(buttonTintColor);
- final View close = caption.findViewById(R.id.close_window);
- final Drawable closeBackground = close.getBackground();
- closeBackground.setTintList(buttonTintColor);
+ private void loadAppInfo(TextView appNameTextView, ImageView appIconImageView) {
+ String packageName = mTaskInfo.realActivity.getPackageName();
+ PackageManager pm = mContext.getApplicationContext().getPackageManager();
+ try {
+ // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(0));
+ appNameTextView.setText(pm.getApplicationLabel(applicationInfo));
+ appIconImageView.setImageDrawable(pm.getApplicationIcon(applicationInfo));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Package not found: " + packageName, e);
}
}
@@ -371,9 +284,18 @@
final int shadowRadius = loadDimensionPixelSize(resources, mHandleMenuShadowRadiusId);
final int cornerRadius = loadDimensionPixelSize(resources, mHandleMenuCornerRadiusId);
- final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
- - mResult.mDecorContainerOffsetX;
- final int y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+ final int x, y;
+ if (mRelayoutParams.mLayoutResId
+ == R.layout.desktop_mode_app_controls_window_decor) {
+ // Align the handle menu to the left of the caption.
+ x = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX;
+ y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+ } else {
+ // Position the handle menu at the center of the caption.
+ x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
+ - mResult.mDecorContainerOffsetX;
+ y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+ }
mHandleMenuPosition.set(x, y);
String namePrefix = "Caption Menu";
mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
@@ -503,6 +425,15 @@
super.close();
}
+ private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
+ if (DesktopModeStatus.isProto1Enabled()) {
+ return R.layout.desktop_mode_app_controls_window_decor;
+ }
+ return windowingMode == WINDOWING_MODE_FREEFORM
+ ? R.layout.desktop_mode_app_controls_window_decor
+ : R.layout.desktop_mode_focused_window_decor;
+ }
+
static class Factory {
DesktopModeWindowDecoration create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index ddd3b44..31e93ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -84,6 +84,7 @@
};
RunningTaskInfo mTaskInfo;
+ int mLayoutResId;
final SurfaceControl mTaskSurface;
Display mDisplay;
@@ -162,6 +163,8 @@
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
}
+ final int oldLayoutResId = mLayoutResId;
+ mLayoutResId = params.mLayoutResId;
if (!mTaskInfo.isVisible) {
releaseViews();
@@ -178,7 +181,8 @@
final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo);
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|| mDisplay == null
- || mDisplay.getDisplayId() != mTaskInfo.displayId) {
+ || mDisplay.getDisplayId() != mTaskInfo.displayId
+ || oldLayoutResId != mLayoutResId) {
releaseViews();
if (!obtainDisplayOrRegisterListener()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
new file mode 100644
index 0000000..95b5051
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -0,0 +1,94 @@
+package com.android.wm.shell.windowdecor.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.pm.PackageManager
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
+import android.util.Log
+import android.view.View
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.wm.shell.R
+
+/**
+ * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
+ * finer controls such as a close window button and an "app info" section to pull up additional
+ * controls.
+ */
+internal class DesktopModeAppControlsWindowDecorationViewHolder(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: View.OnClickListener
+) : DesktopModeWindowDecorationViewHolder(rootView) {
+
+ private val captionView: View = rootView.findViewById(R.id.desktop_mode_caption)
+ private val captionHandle: View = rootView.findViewById(R.id.caption_handle)
+ private val openMenuButton: View = rootView.findViewById(R.id.open_menu_button)
+ private val closeWindowButton: ImageButton = rootView.findViewById(R.id.close_window)
+ private val expandMenuButton: ImageButton = rootView.findViewById(R.id.expand_menu_button)
+ private val appNameTextView: TextView = rootView.findViewById(R.id.application_name)
+ private val appIconImageView: ImageView = rootView.findViewById(R.id.application_icon)
+
+ init {
+ captionView.setOnTouchListener(onCaptionTouchListener)
+ captionHandle.setOnTouchListener(onCaptionTouchListener)
+ openMenuButton.setOnClickListener(onCaptionButtonClickListener)
+ closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+ }
+
+ override fun bindData(taskInfo: RunningTaskInfo) {
+ bindAppInfo(taskInfo)
+
+ val captionDrawable = captionView.background as GradientDrawable
+ captionDrawable.setColor(taskInfo.taskDescription.statusBarColor)
+
+ closeWindowButton.imageTintList = ColorStateList.valueOf(
+ getCaptionCloseButtonColor(taskInfo))
+ expandMenuButton.imageTintList = ColorStateList.valueOf(
+ getCaptionExpandButtonColor(taskInfo))
+ appNameTextView.setTextColor(getCaptionAppNameTextColor(taskInfo))
+ }
+
+ private fun bindAppInfo(taskInfo: RunningTaskInfo) {
+ val packageName: String = taskInfo.realActivity.packageName
+ val pm: PackageManager = context.applicationContext.packageManager
+ try {
+ // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
+ val applicationInfo = pm.getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(0))
+ appNameTextView.text = pm.getApplicationLabel(applicationInfo)
+ appIconImageView.setImageDrawable(pm.getApplicationIcon(applicationInfo))
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Package not found: $packageName", e)
+ }
+ }
+
+ private fun getCaptionAppNameTextColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_app_name_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_app_name_dark)
+ }
+ }
+
+ private fun getCaptionCloseButtonColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_close_button_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_close_button_dark)
+ }
+ }
+
+ private fun getCaptionExpandButtonColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_expand_button_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_expand_button_dark)
+ }
+ }
+
+ companion object {
+ private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
new file mode 100644
index 0000000..47a12a0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -0,0 +1,44 @@
+package com.android.wm.shell.windowdecor.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.widget.ImageButton
+import com.android.wm.shell.R
+
+/**
+ * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen). It
+ * hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
+ */
+internal class DesktopModeFocusedWindowDecorationViewHolder(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: View.OnClickListener
+) : DesktopModeWindowDecorationViewHolder(rootView) {
+
+ private val captionView: View = rootView.findViewById(R.id.desktop_mode_caption)
+ private val captionHandle: ImageButton = rootView.findViewById(R.id.caption_handle)
+
+ init {
+ captionView.setOnTouchListener(onCaptionTouchListener)
+ captionHandle.setOnTouchListener(onCaptionTouchListener)
+ captionHandle.setOnClickListener(onCaptionButtonClickListener)
+ }
+
+ override fun bindData(taskInfo: RunningTaskInfo) {
+ val captionColor = taskInfo.taskDescription.statusBarColor
+ val captionDrawable = captionView.background as GradientDrawable
+ captionDrawable.setColor(captionColor)
+
+ captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
+ }
+
+ private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_handle_bar_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_handle_bar_dark)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
new file mode 100644
index 0000000..514ea52
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
@@ -0,0 +1,28 @@
+package com.android.wm.shell.windowdecor.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.graphics.Color
+import android.view.View
+
+/**
+ * Encapsulates the root [View] of a window decoration and its children to facilitate looking up
+ * children (via findViewById) and updating to the latest data from [RunningTaskInfo].
+ */
+internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) {
+ val context: Context = rootView.context
+
+ /**
+ * A signal to the view holder that new data is available and that the views should be updated
+ * to reflect it.
+ */
+ abstract fun bindData(taskInfo: RunningTaskInfo)
+
+ /**
+ * Whether the caption items should use the 'light' color variant so that there's good contrast
+ * with the caption background color.
+ */
+ protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean {
+ return Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index ffdb87f..5b06c9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -79,7 +79,8 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
+ appExistAtStart = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 792e2b0..c840183 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -82,7 +82,8 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, sendNotificationApp,
+ fromOtherApp = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 8b025cd..919bf06 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -185,7 +185,8 @@
Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
appBubbleIntent.setPackage(mContext.getPackageName());
- mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mMainExecutor);
+ mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mock(Icon.class),
+ mMainExecutor);
mPositioner = new TestableBubblePositioner(mContext,
mock(WindowManager.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 2e2e49e..eda6fdc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -145,39 +145,48 @@
}
@Test
- public void testMoveToStage() {
+ public void testMoveToStage_splitActiveBackground() {
+ when(mStageCoordinator.isSplitActive()).thenReturn(true);
+
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
- mStageCoordinator.moveToStage(task, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- new WindowContainerTransaction());
- verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
- assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
-
- mStageCoordinator.moveToStage(task, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- new WindowContainerTransaction());
- verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
+ mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ verify(mSideStage).addTask(eq(task), eq(wct));
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
+ assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
}
@Test
- public void testMoveToUndefinedStage() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-
- // Verify move to undefined stage while split screen not activated moves task to side stage.
- when(mStageCoordinator.isSplitScreenVisible()).thenReturn(false);
- mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
- mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- new WindowContainerTransaction());
- verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
- assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
-
- // Verify move to undefined stage after split screen activated moves task based on position.
+ public void testMoveToStage_splitActiveForeground() {
+ when(mStageCoordinator.isSplitActive()).thenReturn(true);
when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
- assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
- mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
- new WindowContainerTransaction());
- verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
- assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
+ // Assume current side stage is top or left.
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ verify(mMainStage).addTask(eq(task), eq(wct));
+ assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
+ assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
+
+ mStageCoordinator.moveToStage(task, SPLIT_POSITION_TOP_OR_LEFT, wct);
+ verify(mSideStage).addTask(eq(task), eq(wct));
+ assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
+ assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
+ }
+
+ @Test
+ public void testMoveToStage_splitInctive() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
+ eq(SPLIT_POSITION_BOTTOM_OR_RIGHT));
+ assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
}
@Test
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8ab7159..8efd180 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6251,6 +6251,7 @@
* Volume behavior for an audio device where no software attenuation is applied, and
* the volume is kept synchronized between the host and the device itself through a
* device-specific protocol such as BT AVRCP.
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
*/
@SystemApi
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
@@ -6261,6 +6262,7 @@
* device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
* normal vs in phone call).
* @see #setMode(int)
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
*/
@SystemApi
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
@@ -6270,11 +6272,6 @@
* A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
* the volume percentage of the audio device. Specifically, {@link #setStreamVolume} will have
* no effect, or an unreliable effect.
- *
- * {@link #DEVICE_VOLUME_BEHAVIOR_FULL} will be returned instead by
- * {@link #getDeviceVolumeBehavior} for target SDK versions before U.
- *
- * @see #RETURN_DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY
*/
@SystemApi
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
@@ -6316,27 +6313,18 @@
public @interface AbsoluteDeviceVolumeBehavior {}
/**
- * Volume behaviors that can be set with {@link #setDeviceVolumeBehavior}.
* @hide
- */
- @IntDef({
- DEVICE_VOLUME_BEHAVIOR_VARIABLE,
- DEVICE_VOLUME_BEHAVIOR_FULL,
- DEVICE_VOLUME_BEHAVIOR_FIXED,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SettableDeviceVolumeBehavior {}
-
- /**
- * @hide
- * Throws IAE on a non-settable volume behavior value
+ * Throws IAE on an invalid volume behavior value
* @param volumeBehavior behavior value to check
*/
- public static void enforceSettableVolumeBehavior(int volumeBehavior) {
+ public static void enforceValidVolumeBehavior(int volumeBehavior) {
switch (volumeBehavior) {
case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
case DEVICE_VOLUME_BEHAVIOR_FULL:
case DEVICE_VOLUME_BEHAVIOR_FIXED:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
return;
default:
throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
@@ -6346,8 +6334,11 @@
/**
* @hide
* Sets the volume behavior for an audio output device.
- *
- * @see SettableDeviceVolumeBehavior
+ * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
+ * @see #DEVICE_VOLUME_BEHAVIOR_FULL
+ * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
+ * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
+ * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
* @param device the device to be affected
* @param deviceVolumeBehavior one of the device behaviors
*/
@@ -6357,10 +6348,10 @@
Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @SettableDeviceVolumeBehavior int deviceVolumeBehavior) {
+ @DeviceVolumeBehavior int deviceVolumeBehavior) {
// verify arguments (validity of device type is enforced in server)
Objects.requireNonNull(device);
- enforceSettableVolumeBehavior(deviceVolumeBehavior);
+ enforceValidVolumeBehavior(deviceVolumeBehavior);
// communicate with service
final IAudioService service = getService();
try {
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 9b238e1..6744359 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -49,6 +49,7 @@
import libcore.io.IoUtils;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
@@ -255,17 +256,19 @@
// get orientation
if (MediaFile.isExifMimeType(mimeType)) {
- exif = new ExifInterface(file);
- switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- orientation = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- orientation = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- orientation = 270;
- break;
+ try (FileInputStream is = new FileInputStream(file)) {
+ exif = new ExifInterface(is.getFD());
+ switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ orientation = 90;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ orientation = 180;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ orientation = 270;
+ break;
+ }
}
}
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml
new file mode 100644
index 0000000..aafe466
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/system_accent1_200">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M7,20H4Q3.175,20 2.588,19.413Q2,18.825 2,18V6Q2,5.175 2.588,4.588Q3.175,4 4,4H20V6H4Q4,6 4,6Q4,6 4,6V18Q4,18 4,18Q4,18 4,18H7ZM9,20V18.2Q8.55,17.775 8.275,17.225Q8,16.675 8,16Q8,15.325 8.275,14.775Q8.55,14.225 9,13.8V12H13V13.8Q13.45,14.225 13.725,14.775Q14,15.325 14,16Q14,16.675 13.725,17.225Q13.45,17.775 13,18.2V20ZM11,17.5Q11.65,17.5 12.075,17.075Q12.5,16.65 12.5,16Q12.5,15.35 12.075,14.925Q11.65,14.5 11,14.5Q10.35,14.5 9.925,14.925Q9.5,15.35 9.5,16Q9.5,16.65 9.925,17.075Q10.35,17.5 11,17.5ZM21,20H16Q15.575,20 15.288,19.712Q15,19.425 15,19V10Q15,9.575 15.288,9.287Q15.575,9 16,9H21Q21.425,9 21.712,9.287Q22,9.575 22,10V19Q22,19.425 21.712,19.712Q21.425,20 21,20ZM17,18H20V11H17Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml
index 97d201d..190e0a8 100644
--- a/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@android:color/system_neutral1_200">
+ android:tint="@android:color/system_accent1_200">
<path
android:fillColor="@android:color/white"
android:pathData="M6.85,15Q7.625,15 8.238,14.55Q8.85,14.1 9.1,13.375L9.475,12.225Q9.875,11.025 9.275,10.012Q8.675,9 7.55,9H4.025L4.5,12.925Q4.625,13.8 5.287,14.4Q5.95,15 6.85,15ZM17.15,15Q18.05,15 18.712,14.4Q19.375,13.8 19.5,12.925L19.975,9H16.475Q15.35,9 14.75,10.025Q14.15,11.05 14.55,12.25L14.9,13.375Q15.15,14.1 15.762,14.55Q16.375,15 17.15,15ZM6.85,17Q5.2,17 3.963,15.912Q2.725,14.825 2.525,13.175L2,9H1V7H7.55Q8.65,7 9.562,7.537Q10.475,8.075 11,9H13.025Q13.55,8.075 14.463,7.537Q15.375,7 16.475,7H23V9H22L21.475,13.175Q21.275,14.825 20.038,15.912Q18.8,17 17.15,17Q15.725,17 14.588,16.188Q13.45,15.375 13,14.025L12.625,12.9Q12.575,12.725 12.525,12.537Q12.475,12.35 12.425,12H11.575Q11.525,12.3 11.475,12.487Q11.425,12.675 11.375,12.85L11,14Q10.55,15.35 9.413,16.175Q8.275,17 6.85,17Z"/>
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml
index 1611861..78120a3d 100644
--- a/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_200">
<path android:fillColor="@android:color/system_accent1_200"
android:pathData="M12,16.4 L7.6,12 12,7.6 16.4,12ZM13.4,21.375Q13.125,21.65 12.75,21.8Q12.375,21.95 12,21.95Q11.625,21.95 11.25,21.8Q10.875,21.65 10.6,21.375L2.625,13.4Q2.35,13.125 2.2,12.75Q2.05,12.375 2.05,12Q2.05,11.625 2.2,11.25Q2.35,10.875 2.625,10.6L10.575,2.65Q10.875,2.35 11.238,2.2Q11.6,2.05 12,2.05Q12.4,2.05 12.762,2.2Q13.125,2.35 13.425,2.65L21.375,10.6Q21.65,10.875 21.8,11.25Q21.95,11.625 21.95,12Q21.95,12.375 21.8,12.75Q21.65,13.125 21.375,13.4ZM12,19.2 L19.2,12Q19.2,12 19.2,12Q19.2,12 19.2,12L12,4.8Q12,4.8 12,4.8Q12,4.8 12,4.8L4.8,12Q4.8,12 4.8,12Q4.8,12 4.8,12L12,19.2Q12,19.2 12,19.2Q12,19.2 12,19.2Z"/>
</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml
new file mode 100644
index 0000000..f1fda17
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/system_accent1_200">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M9,22 L7.65,17.45Q6.45,16.5 5.725,15.075Q5,13.65 5,12Q5,10.35 5.725,8.925Q6.45,7.5 7.65,6.55L9,2H15L16.35,6.55Q17.55,7.5 18.275,8.925Q19,10.35 19,12Q19,13.65 18.275,15.075Q17.55,16.5 16.35,17.45L15,22ZM12,17Q14.075,17 15.538,15.537Q17,14.075 17,12Q17,9.925 15.538,8.462Q14.075,7 12,7Q9.925,7 8.463,8.462Q7,9.925 7,12Q7,14.075 8.463,15.537Q9.925,17 12,17ZM10.1,5.25Q11.075,4.975 12,4.975Q12.925,4.975 13.9,5.25L13.5,4H10.5ZM10.5,20H13.5L13.9,18.75Q12.925,19.025 12,19.025Q11.075,19.025 10.1,18.75ZM10.1,4H10.5H13.5H13.9Q12.925,4 12,4Q11.075,4 10.1,4ZM10.5,20H10.1Q11.075,20 12,20Q12.925,20 13.9,20H13.5Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml b/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml
index 15f6987..dc12d12 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_600">
<path android:fillColor="@android:color/white"
android:pathData="M7,20H4Q3.175,20 2.588,19.413Q2,18.825 2,18V6Q2,5.175 2.588,4.588Q3.175,4 4,4H20V6H4Q4,6 4,6Q4,6 4,6V18Q4,18 4,18Q4,18 4,18H7ZM9,20V18.2Q8.55,17.775 8.275,17.225Q8,16.675 8,16Q8,15.325 8.275,14.775Q8.55,14.225 9,13.8V12H13V13.8Q13.45,14.225 13.725,14.775Q14,15.325 14,16Q14,16.675 13.725,17.225Q13.45,17.775 13,18.2V20ZM11,17.5Q11.65,17.5 12.075,17.075Q12.5,16.65 12.5,16Q12.5,15.35 12.075,14.925Q11.65,14.5 11,14.5Q10.35,14.5 9.925,14.925Q9.5,15.35 9.5,16Q9.5,16.65 9.925,17.075Q10.35,17.5 11,17.5ZM21,20H16Q15.575,20 15.288,19.712Q15,19.425 15,19V10Q15,9.575 15.288,9.287Q15.575,9 16,9H21Q21.425,9 21.712,9.287Q22,9.575 22,10V19Q22,19.425 21.712,19.712Q21.425,20 21,20ZM17,18H20V11H17Z"/>
</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml b/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml
index 9065520..0baf7ef 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_600">
<path
android:fillColor="@android:color/white"
android:pathData="M6.85,15Q7.625,15 8.238,14.55Q8.85,14.1 9.1,13.375L9.475,12.225Q9.875,11.025 9.275,10.012Q8.675,9 7.55,9H4.025L4.5,12.925Q4.625,13.8 5.287,14.4Q5.95,15 6.85,15ZM17.15,15Q18.05,15 18.712,14.4Q19.375,13.8 19.5,12.925L19.975,9H16.475Q15.35,9 14.75,10.025Q14.15,11.05 14.55,12.25L14.9,13.375Q15.15,14.1 15.762,14.55Q16.375,15 17.15,15ZM6.85,17Q5.2,17 3.963,15.912Q2.725,14.825 2.525,13.175L2,9H1V7H7.55Q8.65,7 9.562,7.537Q10.475,8.075 11,9H13.025Q13.55,8.075 14.463,7.537Q15.375,7 16.475,7H23V9H22L21.475,13.175Q21.275,14.825 20.038,15.912Q18.8,17 17.15,17Q15.725,17 14.588,16.188Q13.45,15.375 13,14.025L12.625,12.9Q12.575,12.725 12.525,12.537Q12.475,12.35 12.425,12H11.575Q11.525,12.3 11.475,12.487Q11.425,12.675 11.375,12.85L11,14Q10.55,15.35 9.413,16.175Q8.275,17 6.85,17Z"/>
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_watch.xml b/packages/CompanionDeviceManager/res/drawable/ic_watch.xml
index d7a28d9..0f6b21f 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_watch.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_watch.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_600">
<path android:fillColor="@android:color/white"
android:pathData="M9,22 L7.65,17.45Q6.45,16.5 5.725,15.075Q5,13.65 5,12Q5,10.35 5.725,8.925Q6.45,7.5 7.65,6.55L9,2H15L16.35,6.55Q17.55,7.5 18.275,8.925Q19,10.35 19,12Q19,13.65 18.275,15.075Q17.55,16.5 16.35,17.45L15,22ZM12,17Q14.075,17 15.538,15.537Q17,14.075 17,12Q17,9.925 15.538,8.462Q14.075,7 12,7Q9.925,7 8.463,8.462Q7,9.925 7,12Q7,14.075 8.463,15.537Q9.925,17 12,17ZM10.1,5.25Q11.075,4.975 12,4.975Q12.925,4.975 13.9,5.25L13.5,4H10.5ZM10.5,20H13.5L13.9,18.75Q12.925,19.025 12,19.025Q11.075,19.025 10.1,18.75ZM10.1,4H10.5H13.5H13.9Q12.925,4 12,4Q11.075,4 10.1,4ZM10.5,20H10.1Q11.075,20 12,20Q12.925,20 13.9,20H13.5Z"/>
</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index d1d2c70..d470d4c 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -38,8 +38,7 @@
android:layout_width="match_parent"
android:layout_height="32dp"
android:gravity="center"
- android:layout_marginTop="18dp"
- android:tint="@android:color/system_accent1_600"/>
+ android:layout_marginTop="18dp" />
<LinearLayout style="@style/Description">
<TextView
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index ac5294a..79f04b9 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -29,7 +29,6 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="24dp"
- android:tint="@android:color/system_accent1_600"
android:importantForAccessibility="no"
android:contentDescription="@null"/>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 99b776c..71ae578 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -39,6 +39,7 @@
import static com.android.companiondevicemanager.Utils.getApplicationLabel;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getIcon;
+import static com.android.companiondevicemanager.Utils.getImageColor;
import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon;
import static com.android.companiondevicemanager.Utils.getVendorHeaderName;
import static com.android.companiondevicemanager.Utils.hasVendorIcon;
@@ -56,7 +57,6 @@
import android.companion.IAssociationRequestCallback;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Color;
@@ -463,8 +463,6 @@
final Drawable vendorIcon;
final CharSequence vendorName;
final Spanned title;
- int nightModeFlags = getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK;
if (!SUPPORTED_SELF_MANAGED_PROFILES.contains(deviceProfile)) {
throw new RuntimeException("Unsupported profile " + deviceProfile);
@@ -477,8 +475,7 @@
vendorName = getVendorHeaderName(this, packageName, userId);
mVendorHeaderImage.setImageDrawable(vendorIcon);
if (hasVendorIcon(this, packageName, userId)) {
- int color = nightModeFlags == Configuration.UI_MODE_NIGHT_YES
- ? android.R.color.system_accent1_200 : android.R.color.system_accent1_600;
+ int color = getImageColor(this);
mVendorHeaderImage.setColorFilter(getResources().getColor(color, /* Theme= */null));
}
} catch (PackageManager.NameNotFoundException e) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index 328c67e..d8348d1 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static com.android.companiondevicemanager.Utils.getIcon;
+import static com.android.companiondevicemanager.Utils.getImageColor;
import android.content.Context;
import android.view.LayoutInflater;
@@ -65,6 +66,10 @@
viewHolder.mImageView.setImageDrawable(
getIcon(mContext, android.R.drawable.stat_sys_data_bluetooth));
}
+
+ viewHolder.mImageView.setColorFilter(
+ mContext.getResources().getColor(getImageColor(mContext), /* Theme= */null));
+
return viewHolder;
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index fceca91..8c14f80 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -22,6 +22,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -120,6 +121,20 @@
}
}
+ private static boolean isDarkTheme(@NonNull Context context) {
+ int nightModeFlags = context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ return nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ // Get image color for the corresponding theme.
+ static int getImageColor(@NonNull Context context) {
+ if (isDarkTheme(context)) {
+ return android.R.color.system_accent1_200;
+ } else {
+ return android.R.color.system_accent1_600;
+ }
+ }
/**
* Getting ApplicationInfo from meta-data.
*/
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index b32fe3f..28f9453 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -467,19 +467,19 @@
GetCredentialRequest.Builder(
Bundle()
).addCredentialOption(
- CredentialOption(
+ CredentialOption.Builder(
passwordOption.type,
passwordOption.requestData,
passwordOption.candidateQueryData,
- passwordOption.isSystemProviderRequired
- )
+ ).setIsSystemProviderRequired(passwordOption.isSystemProviderRequired)
+ .build()
).addCredentialOption(
- CredentialOption(
+ CredentialOption.Builder(
passkeyOption.type,
passkeyOption.requestData,
passkeyOption.candidateQueryData,
- passkeyOption.isSystemProviderRequired
- )
+ ).setIsSystemProviderRequired(passkeyOption.isSystemProviderRequired)
+ .build()
).build(),
"com.google.android.youtube"
)
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index 6669e35..a2118fa 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -37,9 +37,8 @@
<string name="install_confirm_question">Do you want to install this app?</string>
<!-- Message for updating an existing app [CHAR LIMIT=NONE] -->
<string name="install_confirm_question_update">Do you want to update this app?</string>
- <!-- TODO(b/244413073) Revise the description after getting UX input and UXR on this. -->
- <!-- Message for updating an existing app with update owner reminder [DO NOT TRANSLATE][CHAR LIMIT=NONE] -->
- <string name="install_confirm_question_update_owner_reminder">Updates to this app are currently managed by <xliff:g id="existing_update_owner">%1$s</xliff:g>.\n\nDo you want to install this update from <xliff:g id="new_update_owner">%2$s</xliff:g>?</string>
+ <!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] -->
+ <string name="install_confirm_question_update_owner_reminder">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your phone. App functionality may change.</string>
<!-- [CHAR LIMIT=100] -->
<string name="install_failed">App not installed.</string>
<!-- Reason displayed when installation fails because the package was blocked
@@ -82,6 +81,8 @@
<!-- [CHAR LIMIT=15] -->
<string name="ok">OK</string>
+ <!-- [CHAR LIMIT=30] -->
+ <string name="update_anyway">Update anyway</string>
<!-- [CHAR LIMIT=15] -->
<string name="manage_applications">Manage apps</string>
<!-- [CHAR LIMIT=30] -->
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 7cbd4bc..3ba2acb 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -148,10 +148,11 @@
&& mPendingUserActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
viewToEnable.setText(
getString(R.string.install_confirm_question_update_owner_reminder,
- existingUpdateOwnerLabel, requestedUpdateOwnerLabel));
+ requestedUpdateOwnerLabel, existingUpdateOwnerLabel));
+ mOk.setText(R.string.update_anyway);
+ } else {
+ mOk.setText(R.string.update);
}
-
- mOk.setText(R.string.update);
} else {
// This is a new application with no permissions.
viewToEnable = requireViewById(R.id.install_confirm_question);
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index f6bb3cc..47ac2df 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -109,7 +109,7 @@
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
TwoRowsTopAppBar(
- title = { Title(title = title, maxLines = 2) },
+ title = { Title(title = title, maxLines = 3) },
titleTextStyle = MaterialTheme.typography.displaySmall,
smallTitleTextStyle = MaterialTheme.typography.titleMedium,
titleBottomPadding = LargeTitleBottomPadding,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
index c609004..9f33fcb 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
@@ -62,10 +62,12 @@
}
val permission = AppOpsManager.opToPermission(op)
- packageManager.updatePermissionFlags(permission, app.packageName,
- PackageManager.FLAG_PERMISSION_USER_SET, PackageManager.FLAG_PERMISSION_USER_SET,
- UserHandle.getUserHandleForUid(app.uid))
-
+ if (permission != null) {
+ packageManager.updatePermissionFlags(permission, app.packageName,
+ PackageManager.FLAG_PERMISSION_USER_SET,
+ PackageManager.FLAG_PERMISSION_USER_SET,
+ UserHandle.getUserHandleForUid(app.uid))
+ }
_mode.postValue(mode)
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index a8eeec3..80030f7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -50,10 +50,6 @@
@GuardedBy("mLock")
private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>();
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- // Maximum number of backing stores allowed
- static final int NUM_MAX_BACKING_STORE = 8;
-
@GuardedBy("mLock")
private int mNumBackingStore = 0;
@@ -65,8 +61,24 @@
// The generation number is only increased when a new non-predefined setting is inserted
private static final String DEFAULT_MAP_KEY_FOR_UNSET_SETTINGS = "";
- public GenerationRegistry(Object lock) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ // Minimum number of backing stores; supports 3 users
+ static final int MIN_NUM_BACKING_STORE = 8;
+ // Maximum number of backing stores; supports 18 users
+ static final int MAX_NUM_BACKING_STORE = 38;
+
+ private final int mMaxNumBackingStore;
+
+ GenerationRegistry(Object lock, int maxNumUsers) {
mLock = lock;
+ // Add some buffer to maxNumUsers to accommodate corner cases when the actual number of
+ // users in the system exceeds the limit
+ maxNumUsers = maxNumUsers + 2;
+ // Number of backing stores needed for N users is (N + N + 1 + 1) = N * 2 + 2
+ // N Secure backing stores and N System backing stores, 1 Config and 1 Global for all users
+ // However, we always make sure that at least 3 users and at most 18 users are supported.
+ mMaxNumBackingStore = Math.min(Math.max(maxNumUsers * 2 + 2, MIN_NUM_BACKING_STORE),
+ MAX_NUM_BACKING_STORE);
}
/**
@@ -195,7 +207,7 @@
}
if (backingStore == null) {
try {
- if (mNumBackingStore >= NUM_MAX_BACKING_STORE) {
+ if (mNumBackingStore >= mMaxNumBackingStore) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error creating backing store - at capacity");
}
@@ -275,4 +287,9 @@
}
return -1;
}
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ int getMaxNumBackingStores() {
+ return mMaxNumBackingStore;
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7e89bfc..721b3c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2831,7 +2831,7 @@
public SettingsRegistry() {
mHandler = new MyHandler(getContext().getMainLooper());
- mGenerationRegistry = new GenerationRegistry(mLock);
+ mGenerationRegistry = new GenerationRegistry(mLock, UserManager.getMaxSupportedUsers());
mBackupManager = new BackupManager(getContext());
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 07b71d3..aebdf4f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -266,7 +266,6 @@
Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
- Settings.Global.ENABLE_RESTRICTED_BUCKET,
Settings.Global.ENABLE_TARE,
Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 586d6f7..12865f4 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -36,7 +36,7 @@
public class GenerationRegistryTest {
@Test
public void testGenerationsWithRegularSetting() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+ final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
@@ -93,7 +93,7 @@
@Test
public void testGenerationsWithConfigSetting() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+ final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
final String prefix = "test_namespace/";
final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
@@ -110,10 +110,10 @@
@Test
public void testMaxNumBackingStores() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+ final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
- for (int i = 0; i < GenerationRegistry.NUM_MAX_BACKING_STORE; i++) {
+ for (int i = 0; i < generationRegistry.getMaxNumBackingStores(); i++) {
b.clear();
final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, i);
generationRegistry.addGenerationData(b, key, testSecureSetting);
@@ -121,7 +121,7 @@
}
b.clear();
final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE,
- GenerationRegistry.NUM_MAX_BACKING_STORE + 1);
+ generationRegistry.getMaxNumBackingStores() + 1);
generationRegistry.addGenerationData(b, key, testSecureSetting);
// Should fail to add generation because the number of backing stores has reached limit
checkBundle(b, -1, -1, true);
@@ -133,7 +133,7 @@
@Test
public void testMaxSizeBackingStore() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+ final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
@@ -153,7 +153,7 @@
@Test
public void testUnsetSettings() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+ final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
@@ -172,7 +172,7 @@
@Test
public void testGlobalSettings() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object());
+ final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
final int globalKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, 0);
final String testGlobalSetting = "test_global_setting";
final Bundle b = new Bundle();
@@ -188,6 +188,18 @@
assertThat(array).isEqualTo(array2);
}
+ @Test
+ public void testNumberOfBackingStores() {
+ GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 0);
+ // Test that the capacity of the backing stores is always valid
+ assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
+ GenerationRegistry.MIN_NUM_BACKING_STORE);
+ generationRegistry = new GenerationRegistry(new Object(), 100);
+ // Test that the capacity of the backing stores is always valid
+ assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
+ GenerationRegistry.MAX_NUM_BACKING_STORE);
+ }
+
private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull)
throws IOException {
final MemoryIntArray array = getArray(b);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8b38deb..941697b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -376,7 +376,7 @@
name: "SystemUIRobo-stub",
defaults: [
"platform_app_defaults",
- "SystemUI_app_defaults",
+ "SystemUI_optimized_defaults",
"SystemUI_compose_defaults",
],
manifest: "tests/AndroidManifest-base.xml",
@@ -443,7 +443,7 @@
}
systemui_optimized_java_defaults {
- name: "SystemUI_app_defaults",
+ name: "SystemUI_optimized_defaults",
soong_config_variables: {
SYSTEMUI_OPTIMIZE_JAVA: {
optimize: {
@@ -452,12 +452,10 @@
shrink: true,
shrink_resources: true,
proguard_compatibility: false,
- proguard_flags_files: ["proguard.flags"],
},
conditions_default: {
optimize: {
proguard_compatibility: false,
- proguard_flags_files: ["proguard.flags"],
},
},
},
@@ -468,7 +466,7 @@
name: "SystemUI",
defaults: [
"platform_app_defaults",
- "SystemUI_app_defaults",
+ "SystemUI_optimized_defaults",
],
static_libs: [
"SystemUI-core",
@@ -483,6 +481,9 @@
kotlincflags: ["-Xjvm-default=enable"],
dxflags: ["--multi-dex"],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
required: [
"privapp_whitelist_com.android.systemui",
"wmshell.protolog.json.gz",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt
index b9e38cf..99fe26c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt
@@ -53,6 +53,7 @@
modifier: Modifier = Modifier,
) {
val isScrimEnabled: Boolean by viewModel.isScrimEnabled.collectAsState()
+ val scrimAlpha: Float by viewModel.scrimAlpha.collectAsState()
// TODO(b/273298030): find a different way to get the height constraint from its parent.
BoxWithConstraints(modifier = modifier) {
@@ -61,7 +62,7 @@
Scrim(
modifier = Modifier.fillMaxSize(),
remoteTouch = viewModel::onScrimTouched,
- alpha = { viewModel.scrimAlpha.value },
+ alpha = { scrimAlpha },
isScrimEnabled = isScrimEnabled,
)
Shade(
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index a317178..762dcdc 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -55,7 +55,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="@dimen/chipbar_text_size"
- android:textColor="@android:color/system_accent2_900"
+ android:textColor="@color/chipbar_text_and_icon_color"
android:alpha="0.0"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_user_chip_container.xml b/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
index b374074..80f5d87 100644
--- a/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
+++ b/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
@@ -24,7 +24,7 @@
android:orientation="horizontal"
android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
android:background="@drawable/status_bar_user_chip_bg"
- android:visibility="visible" >
+ android:visibility="gone" >
<ImageView android:id="@+id/current_user_avatar"
android:layout_width="@dimen/status_bar_user_chip_avatar_size"
android:layout_height="@dimen/status_bar_user_chip_avatar_size"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 62e8c5f..31071ca 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -231,6 +231,9 @@
<color name="people_tile_background">@color/material_dynamic_secondary95</color>
+ <!-- Chipbar -->
+ <color name="chipbar_text_and_icon_color">@android:color/system_accent2_900</color>
+
<!-- Internet Dialog -->
<!-- Material next state on color-->
<color name="settingslib_state_on_color">@color/settingslib_state_on</color>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 4aaa566..3b9060a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -259,7 +259,7 @@
largeTimeListener?.update(shouldTimeListenerRun)
}
- override fun onTimeFormatChanged(timeFormat: String) {
+ override fun onTimeFormatChanged(timeFormat: String?) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5ec59ab..7b781ce 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,6 +22,7 @@
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import android.annotation.Nullable;
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
@@ -458,6 +459,7 @@
mView.setClock(clock, mStatusBarStateController.getState());
}
+ @Nullable
private ClockController getClock() {
return mClockEventController.getClock();
}
@@ -510,7 +512,9 @@
}
/** Gets the animations for the current clock. */
+ @Nullable
public ClockAnimations getClockAnimations() {
- return getClock().getAnimations();
+ ClockController clock = getClock();
+ return clock == null ? null : clock.getAnimations();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 7255383..1a572b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -260,6 +260,14 @@
*/
@Override
public void finish(boolean strongAuth, int targetUserId) {
+ if (mFeatureFlags.isEnabled(Flags.PREVENT_BYPASS_KEYGUARD)
+ && !mKeyguardStateController.canDismissLockScreen() && !strongAuth) {
+ Log.e(TAG,
+ "Tried to dismiss keyguard when lockscreen is not dismissible and user "
+ + "was not authenticated with a primary security method "
+ + "(pin/password/pattern).");
+ return;
+ }
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0e2f8f0..f2f0c59 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -69,6 +69,7 @@
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
import android.annotation.AnyThread;
@@ -1610,7 +1611,7 @@
requestActiveUnlock(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT,
"assistant",
- false);
+ /* dismissKeyguard */ true);
}
}
@@ -1881,6 +1882,11 @@
updateFaceListeningState(BIOMETRIC_ACTION_STOP,
FACE_AUTH_UPDATED_POSTURE_CHANGED);
}
+ if (mPostureState == DEVICE_POSTURE_OPENED) {
+ mLogger.d("Posture changed to open - attempting to request active unlock");
+ requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE,
+ false);
+ }
}
};
@@ -2007,26 +2013,10 @@
FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_STARTED_WAKING_UP);
-
- final ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin =
- mActiveUnlockConfig.isWakeupConsideredUnlockIntent(pmWakeReason)
- ? ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
- : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE;
- final String reason = "wakingUp - " + PowerManager.wakeReasonToString(pmWakeReason);
- if (mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(pmWakeReason)) {
- requestActiveUnlockDismissKeyguard(
- requestOrigin,
- reason
- );
- } else {
- requestActiveUnlock(
- requestOrigin,
- reason
- );
- }
} else {
mLogger.logSkipUpdateFaceListeningOnWakeup(pmWakeReason);
}
+ requestActiveUnlockFromWakeReason(pmWakeReason, true);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2660,6 +2650,32 @@
}
}
+ private void requestActiveUnlockFromWakeReason(@PowerManager.WakeReason int wakeReason,
+ boolean powerManagerWakeup) {
+ if (!mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(wakeReason)) {
+ mLogger.logActiveUnlockRequestSkippedForWakeReasonDueToFaceConfig(wakeReason);
+ return;
+ }
+
+ final ActiveUnlockConfig.ActiveUnlockRequestOrigin requestOrigin =
+ mActiveUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason)
+ ? ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ : ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE;
+ final String reason = "wakingUp - " + PowerManager.wakeReasonToString(wakeReason)
+ + " powerManagerWakeup=" + powerManagerWakeup;
+ if (mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason)) {
+ requestActiveUnlockDismissKeyguard(
+ requestOrigin,
+ reason
+ );
+ } else {
+ requestActiveUnlock(
+ requestOrigin,
+ reason
+ );
+ }
+ }
+
/**
* Attempts to trigger active unlock from trust agent.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 8d5b8be..e3ca13e 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -62,6 +62,16 @@
)
}
+ fun logActiveUnlockRequestSkippedForWakeReasonDueToFaceConfig(wakeReason: Int) {
+ logBuffer.log(
+ "ActiveUnlock",
+ DEBUG,
+ { int1 = wakeReason },
+ { "Skip requesting active unlock from wake reason that doesn't trigger face auth" +
+ " reason=${PowerManager.wakeReasonToString(int1)}" }
+ )
+ }
+
fun logAuthInterruptDetected(active: Boolean) {
logBuffer.log(TAG, DEBUG, { bool1 = active }, { "onAuthInterruptDetected($bool1)" })
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 61d039b..c98a62f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -195,7 +195,7 @@
scope.launch {
alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
if (isVisible) {
- show(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
+ show(SideFpsUiRequestSource.ALTERNATE_BOUNCER, REASON_AUTH_KEYGUARD)
} else {
hide(SideFpsUiRequestSource.ALTERNATE_BOUNCER)
}
@@ -436,13 +436,17 @@
@BiometricOverlayConstants.ShowReason reason: Int
) {
fun update() {
- val c = context.getColor(R.color.biometric_dialog_accent)
- val chevronFill = context.getColor(R.color.sfps_chevron_fill)
val isKeyguard = reason == REASON_AUTH_KEYGUARD
if (isKeyguard) {
+ val color = context.getColor(R.color.numpad_key_color_secondary) // match bouncer color
+ val chevronFill =
+ com.android.settingslib.Utils.getColorAttrDefaultColor(
+ context,
+ android.R.attr.textColorPrimaryInverse
+ )
for (key in listOf(".blue600", ".blue400")) {
addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
- PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
+ PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
}
}
addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
index 5dabbbb..6a6c3eb 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
@@ -16,10 +16,10 @@
package com.android.systemui.common.shared.model
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
/** Models an icon with a specific tint. */
data class TintedIcon(
val icon: Icon,
- @AttrRes val tintAttr: Int?,
+ @ColorRes val tint: Int?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
index dea8cfd..bcc5932 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
@@ -17,15 +17,14 @@
package com.android.systemui.common.ui.binder
import android.widget.ImageView
-import com.android.settingslib.Utils
import com.android.systemui.common.shared.model.TintedIcon
object TintedIconViewBinder {
/**
* Binds the given tinted icon to the view.
*
- * [TintedIcon.tintAttr] will always be applied, meaning that if it is null, then the tint
- * *will* be reset to null.
+ * [TintedIcon.tint] will always be applied, meaning that if it is null, then the tint *will* be
+ * reset to null.
*/
fun bind(
tintedIcon: TintedIcon,
@@ -33,8 +32,8 @@
) {
IconViewBinder.bind(tintedIcon.icon, view)
view.imageTintList =
- if (tintedIcon.tintAttr != null) {
- Utils.getColorAttr(view.context, tintedIcon.tintAttr)
+ if (tintedIcon.tint != null) {
+ view.resources.getColorStateList(tintedIcon.tint, view.context.theme)
} else {
null
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index ee12db8..868e527 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -608,6 +608,7 @@
if (items.size == 1) {
spinner.setBackground(null)
anchor.setOnClickListener(null)
+ anchor.isClickable = false
return
} else {
spinner.background = parent.context.resources
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
index 055cd52..7f567aa 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -23,6 +23,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
import com.android.systemui.dreams.conditions.DreamCondition;
+import com.android.systemui.flags.RestartDozeListener;
import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.condition.ConditionalCoreStartable;
@@ -39,17 +40,19 @@
private final Monitor mConditionMonitor;
private final DreamCondition mDreamCondition;
private final DreamStatusBarStateCallback mCallback;
+ private RestartDozeListener mRestartDozeListener;
@Inject
public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
@Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor,
- DreamStatusBarStateCallback callback) {
+ DreamStatusBarStateCallback callback,
+ RestartDozeListener restartDozeListener) {
super(pretextMonitor);
mConditionMonitor = monitor;
mDreamCondition = dreamCondition;
mCallback = callback;
-
+ mRestartDozeListener = restartDozeListener;
}
@Override
@@ -61,5 +64,8 @@
mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
.addCondition(mDreamCondition)
.build());
+
+ mRestartDozeListener.init();
+ mRestartDozeListener.maybeRestartSleep();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
index 2befce7..5bbfbda 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.dreams.conditions;
+import android.app.DreamManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@
*/
public class DreamCondition extends Condition {
private final Context mContext;
+ private final DreamManager mDreamManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -39,8 +41,10 @@
};
@Inject
- public DreamCondition(Context context) {
+ public DreamCondition(Context context,
+ DreamManager dreamManager) {
mContext = context;
+ mDreamManager = dreamManager;
}
private void processIntent(Intent intent) {
@@ -62,8 +66,8 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
- final Intent stickyIntent = mContext.registerReceiver(mReceiver, filter);
- processIntent(stickyIntent);
+ mContext.registerReceiver(mReceiver, filter);
+ updateCondition(mDreamManager.isDreaming());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index 06ca0ad..28c45b8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -22,7 +22,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.InitializationChecker
-import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -38,8 +37,6 @@
private val featureFlags: FeatureFlagsDebug,
private val broadcastSender: BroadcastSender,
private val initializationChecker: InitializationChecker,
- private val restartDozeListener: RestartDozeListener,
- private val delayableExecutor: DelayableExecutor
) : CoreStartable {
init {
@@ -55,9 +52,6 @@
// protected broadcast should only be sent for the main process
val intent = Intent(FlagManager.ACTION_SYSUI_STARTED)
broadcastSender.sendBroadcast(intent)
-
- restartDozeListener.init()
- delayableExecutor.executeDelayed({ restartDozeListener.maybeRestartSleep() }, 1000)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
index 133e67f..f97112d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
@@ -18,8 +18,6 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.InitializationChecker
-import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -31,9 +29,6 @@
constructor(
dumpManager: DumpManager,
featureFlags: FeatureFlags,
- private val initializationChecker: InitializationChecker,
- private val restartDozeListener: RestartDozeListener,
- private val delayableExecutor: DelayableExecutor
) : CoreStartable {
init {
@@ -42,12 +37,7 @@
}
}
- override fun start() {
- if (initializationChecker.initializeComponents()) {
- restartDozeListener.init()
- delayableExecutor.executeDelayed({ restartDozeListener.maybeRestartSleep() }, 1000)
- }
- }
+ override fun start() {}
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f1086a3..399f2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -228,6 +228,11 @@
@JvmField
val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = true)
+ /** Whether to inflate the bouncer view on a background thread. */
+ // TODO(b/273341787): Tracking Bug
+ @JvmField
+ val PREVENT_BYPASS_KEYGUARD = unreleasedFlag(230, "prevent_bypass_keyguard")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -410,7 +415,7 @@
@JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple")
// TODO(b/270882464): Tracking Bug
- val ENABLE_DOCK_SETUP_V2 = unreleasedFlag(1005, "enable_dock_setup_v2", teamfood = true)
+ val ENABLE_DOCK_SETUP_V2 = releasedFlag(1005, "enable_dock_setup_v2")
// TODO(b/265045965): Tracking Bug
val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
index bd74f4e..b49d60d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/RestartDozeListener.kt
@@ -20,7 +20,9 @@
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -33,6 +35,7 @@
private val statusBarStateController: StatusBarStateController,
private val powerManager: PowerManager,
private val systemClock: SystemClock,
+ @Background val bgExecutor: DelayableExecutor,
) {
companion object {
@@ -44,7 +47,7 @@
val listener =
object : StatusBarStateController.StateListener {
override fun onDreamingChanged(isDreaming: Boolean) {
- settings.putBool(RESTART_NAP_KEY, isDreaming)
+ storeSleepState(isDreaming)
}
}
@@ -62,11 +65,19 @@
}
fun maybeRestartSleep() {
- if (settings.getBool(RESTART_NAP_KEY, false)) {
- Log.d("RestartDozeListener", "Restarting sleep state")
- powerManager.wakeUp(systemClock.uptimeMillis())
- powerManager.goToSleep(systemClock.uptimeMillis())
- settings.putBool(RESTART_NAP_KEY, false)
- }
+ bgExecutor.executeDelayed(
+ {
+ if (settings.getBool(RESTART_NAP_KEY, false)) {
+ Log.d("RestartDozeListener", "Restarting sleep state")
+ powerManager.wakeUp(systemClock.uptimeMillis())
+ powerManager.goToSleep(systemClock.uptimeMillis())
+ }
+ },
+ 1000
+ )
+ }
+
+ private fun storeSleepState(sleeping: Boolean) {
+ bgExecutor.execute { settings.putBool(RESTART_NAP_KEY, sleeping) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index d745a19..aad4a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.LegacyAlternateBouncer
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
@@ -34,6 +35,7 @@
class AlternateBouncerInteractor
@Inject
constructor(
+ private val statusBarStateController: StatusBarStateController,
private val keyguardStateController: KeyguardStateController,
private val bouncerRepository: KeyguardBouncerRepository,
private val biometricSettingsRepository: BiometricSettingsRepository,
@@ -49,6 +51,17 @@
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
+ private val keyguardStateControllerCallback: KeyguardStateController.Callback =
+ object : KeyguardStateController.Callback {
+ override fun onUnlockedChanged() {
+ maybeHide()
+ }
+ }
+
+ init {
+ keyguardStateController.addCallback(keyguardStateControllerCallback)
+ }
+
/**
* Sets the correct bouncer states to show the alternate bouncer if it can show.
*
@@ -109,7 +122,8 @@
biometricSettingsRepository.isStrongBiometricAllowed.value &&
biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
!deviceEntryFingerprintAuthRepository.isLockedOut.value &&
- !keyguardStateController.isUnlocked
+ !keyguardStateController.isUnlocked &&
+ !statusBarStateController.isDozing
} else {
legacyAlternateBouncer != null &&
keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true)
@@ -129,6 +143,12 @@
}
}
+ private fun maybeHide() {
+ if (isVisibleState() && !canShowAlternateBouncerForFingerprint()) {
+ hide()
+ }
+ }
+
companion object {
private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L
private const val NOT_VISIBLE = -1L
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 889adc7..5704f88 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -160,6 +160,14 @@
return factory.create("QSLog", 700 /* maxSize */, false /* systrace */);
}
+ /** Provides a logging buffer for logs related to Quick Settings configuration. */
+ @Provides
+ @SysUISingleton
+ @QSConfigLog
+ public static LogBuffer provideQSConfigLogBuffer(LogBufferFactory factory) {
+ return factory.create("QSConfigLog", 100 /* maxSize */, true /* systrace */);
+ }
+
/** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java
new file mode 100644
index 0000000..295bf88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSConfigLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.plugins.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for QS configuration changed messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface QSConfigLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index ee93c37..dbc2a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -19,12 +19,13 @@
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo.Companion.DEFAULT_ICON_TINT
/** Utility methods for media tap-to-transfer. */
class MediaTttUtils {
@@ -78,7 +79,7 @@
return IconInfo(
contentDescription,
MediaTttIcon.Loaded(packageManager.getApplicationIcon(appPackageName)),
- tintAttr = null,
+ tint = null,
isAppIcon = true
)
} catch (e: PackageManager.NameNotFoundException) {
@@ -96,7 +97,7 @@
)
},
MediaTttIcon.Resource(R.drawable.ic_cast),
- tintAttr = android.R.attr.textColorPrimary,
+ tint = DEFAULT_ICON_TINT,
isAppIcon = false
)
}
@@ -107,7 +108,7 @@
data class IconInfo(
val contentDescription: ContentDescription,
val icon: MediaTttIcon,
- @AttrRes val tintAttr: Int?,
+ @ColorRes val tint: Int?,
/**
* True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
*/
@@ -120,7 +121,7 @@
is MediaTttIcon.Loaded -> Icon.Loaded(icon.drawable, contentDescription)
is MediaTttIcon.Resource -> Icon.Resource(icon.res, contentDescription)
}
- return TintedIcon(iconOutput, tintAttr)
+ return TintedIcon(iconOutput, tint)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt
index b9f6d83..ebb8639 100644
--- a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.multishade.data.model.MultiShadeInteractionModel
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
import com.android.systemui.multishade.data.repository.MultiShadeRepository
+import com.android.systemui.multishade.shared.math.isZero
import com.android.systemui.multishade.shared.model.ProxiedInputModel
import com.android.systemui.multishade.shared.model.ShadeConfig
import com.android.systemui.multishade.shared.model.ShadeId
@@ -63,6 +64,10 @@
}
}
+ /** Whether any shade is expanded, even a little bit. */
+ val isAnyShadeExpanded: Flow<Boolean> =
+ maxShadeExpansion.map { maxExpansion -> !maxExpansion.isZero() }.distinctUntilChanged()
+
/**
* A _processed_ version of the proxied input flow.
*
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
new file mode 100644
index 0000000..ff7c901
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.multishade.domain.interactor
+
+import android.content.Context
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.multishade.shared.model.ProxiedInputModel
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Encapsulates business logic to handle [MotionEvent]-based user input.
+ *
+ * This class is meant purely for the legacy `View`-based system to be able to pass `MotionEvent`s
+ * into the newer multi-shade framework for processing.
+ */
+class MultiShadeMotionEventInteractor
+@Inject
+constructor(
+ @Application private val applicationContext: Context,
+ @Application private val applicationScope: CoroutineScope,
+ private val interactor: MultiShadeInteractor,
+) {
+
+ private val isAnyShadeExpanded: StateFlow<Boolean> =
+ interactor.isAnyShadeExpanded.stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ private var interactionState: InteractionState? = null
+
+ /**
+ * Returns `true` if the given [MotionEvent] and the rest of events in this gesture should be
+ * passed to this interactor's [onTouchEvent] method.
+ *
+ * Note: the caller should continue to pass [MotionEvent] instances into this method, even if it
+ * returns `false` as the gesture may be intercepted mid-stream.
+ */
+ fun shouldIntercept(event: MotionEvent): Boolean {
+ if (isAnyShadeExpanded.value) {
+ // If any shade is expanded, we assume that touch handling outside the shades is handled
+ // by the scrim that appears behind the shades. No need to intercept anything here.
+ return false
+ }
+
+ return when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ // Record where the pointer was placed and which pointer it was.
+ interactionState =
+ InteractionState(
+ initialX = event.x,
+ initialY = event.y,
+ currentY = event.y,
+ pointerId = event.getPointerId(0),
+ isDraggingHorizontally = false,
+ isDraggingVertically = false,
+ )
+
+ false
+ }
+ MotionEvent.ACTION_MOVE -> {
+ interactionState?.let {
+ val pointerIndex = event.findPointerIndex(it.pointerId)
+ val currentX = event.getX(pointerIndex)
+ val currentY = event.getY(pointerIndex)
+ if (!it.isDraggingHorizontally && !it.isDraggingVertically) {
+ val xDistanceTravelled = abs(currentX - it.initialX)
+ val yDistanceTravelled = abs(currentY - it.initialY)
+ val touchSlop = ViewConfiguration.get(applicationContext).scaledTouchSlop
+ interactionState =
+ when {
+ yDistanceTravelled > touchSlop ->
+ it.copy(isDraggingVertically = true)
+ xDistanceTravelled > touchSlop ->
+ it.copy(isDraggingHorizontally = true)
+ else -> interactionState
+ }
+ }
+ }
+
+ // We want to intercept the rest of the gesture if we're dragging.
+ interactionState.isDraggingVertically()
+ }
+ MotionEvent.ACTION_UP,
+ MotionEvent.ACTION_CANCEL ->
+ // Make sure that we intercept the up or cancel if we're dragging, to handle drag
+ // end and cancel.
+ interactionState.isDraggingVertically()
+ else -> false
+ }
+ }
+
+ /**
+ * Notifies that a [MotionEvent] in a series of events of a gesture that was intercepted due to
+ * the result of [shouldIntercept] has been received.
+ *
+ * @param event The [MotionEvent] to handle.
+ * @param viewWidthPx The width of the view, in pixels.
+ * @return `true` if the event was consumed, `false` otherwise.
+ */
+ fun onTouchEvent(event: MotionEvent, viewWidthPx: Int): Boolean {
+ return when (event.actionMasked) {
+ MotionEvent.ACTION_MOVE -> {
+ interactionState?.let {
+ if (it.isDraggingVertically) {
+ val pointerIndex = event.findPointerIndex(it.pointerId)
+ val previousY = it.currentY
+ val currentY = event.getY(pointerIndex)
+ interactionState =
+ it.copy(
+ currentY = currentY,
+ )
+
+ val yDragAmountPx = currentY - previousY
+ if (yDragAmountPx != 0f) {
+ interactor.sendProxiedInput(
+ ProxiedInputModel.OnDrag(
+ xFraction = event.x / viewWidthPx,
+ yDragAmountPx = yDragAmountPx,
+ )
+ )
+ }
+ }
+ }
+
+ true
+ }
+ MotionEvent.ACTION_UP -> {
+ if (interactionState.isDraggingVertically()) {
+ // We finished dragging. Record that so the multi-shade framework can issue a
+ // fling, if the velocity reached in the drag was high enough, for example.
+ interactor.sendProxiedInput(ProxiedInputModel.OnDragEnd)
+ }
+
+ interactionState = null
+ true
+ }
+ MotionEvent.ACTION_CANCEL -> {
+ if (interactionState.isDraggingVertically()) {
+ // Our drag gesture was canceled by the system. This happens primarily in one of
+ // two occasions: (a) the parent view has decided to intercept the gesture
+ // itself and/or route it to a different child view or (b) the pointer has
+ // traveled beyond the bounds of our view and/or the touch display. Either way,
+ // we pass the cancellation event to the multi-shade framework to record it.
+ // Doing that allows the multi-shade framework to know that the gesture ended to
+ // allow new gestures to be accepted.
+ interactor.sendProxiedInput(ProxiedInputModel.OnDragCancel)
+ }
+
+ interactionState = null
+ true
+ }
+ else -> false
+ }
+ }
+
+ private data class InteractionState(
+ val initialX: Float,
+ val initialY: Float,
+ val currentY: Float,
+ val pointerId: Int,
+ val isDraggingHorizontally: Boolean,
+ val isDraggingVertically: Boolean,
+ )
+
+ private fun InteractionState?.isDraggingVertically(): Boolean {
+ return this?.isDraggingVertically == true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt
new file mode 100644
index 0000000..c2eaf72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.multishade.shared.math
+
+import androidx.annotation.VisibleForTesting
+import kotlin.math.abs
+
+/** Returns `true` if this [Float] is within [epsilon] of `0`. */
+fun Float.isZero(epsilon: Float = EPSILON): Boolean {
+ return abs(this) < epsilon
+}
+
+@VisibleForTesting private const val EPSILON = 0.0001f
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt
index ce6ab97..ed92c54 100644
--- a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt
@@ -26,7 +26,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -87,10 +86,7 @@
when (shadeConfig) {
// In the dual shade configuration, the scrim is enabled when the expansion is
// greater than zero on any one of the shades.
- is ShadeConfig.DualShadeConfig ->
- interactor.maxShadeExpansion
- .map { expansion -> expansion > 0 }
- .distinctUntilChanged()
+ is ShadeConfig.DualShadeConfig -> interactor.isAnyShadeExpanded
// No scrim in the single shade configuration.
is ShadeConfig.SingleShadeConfig -> flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index e74d78d..58ac5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -138,7 +138,8 @@
logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" }
when (info.launchMode) {
is NoteTaskLaunchMode.AppBubble -> {
- bubbles.showOrHideAppBubble(intent, userTracker.userHandle)
+ // TODO: provide app bubble icon
+ bubbles.showOrHideAppBubble(intent, userTracker.userHandle, null /* icon */)
// App bubble logging happens on `onBubbleExpandChanged`.
logDebug { "onShowNoteTask - opened as app bubble: $info" }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 2668d2e..fdab9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -91,16 +91,19 @@
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
- mQSLogger.logOnConfigurationChanged(
- /* lastOrientation= */ mLastOrientation,
- /* newOrientation= */ newConfig.orientation,
- /* containerName= */ mView.getDumpableTag());
-
- boolean previousSplitShadeState = mShouldUseSplitNotificationShade;
+ final boolean previousSplitShadeState = mShouldUseSplitNotificationShade;
+ final int previousOrientation = mLastOrientation;
mShouldUseSplitNotificationShade =
- LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
mLastOrientation = newConfig.orientation;
+ mQSLogger.logOnConfigurationChanged(
+ /* oldOrientation= */ previousOrientation,
+ /* newOrientation= */ mLastOrientation,
+ /* oldShouldUseSplitShade= */ previousSplitShadeState,
+ /* newShouldUseSplitShade= */ mShouldUseSplitNotificationShade,
+ /* containerName= */ mView.getDumpableTag());
+
switchTileLayoutIfNeeded();
onConfigurationChanged();
if (previousSplitShadeState != mShouldUseSplitNotificationShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 2083cc7..5e4f531 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -136,9 +136,8 @@
mServices.remove(tile);
mTokenMap.remove(service.getToken());
mTiles.remove(tile.getComponent());
- final String slot = tile.getComponent().getClassName();
- // TileServices doesn't know how to add more than 1 icon per slot, so remove all
- mMainHandler.post(() -> mStatusBarIconController.removeAllIconsForSlot(slot));
+ final String slot = getStatusBarIconSlotName(tile.getComponent());
+ mMainHandler.post(() -> mStatusBarIconController.removeIconForTile(slot));
}
}
@@ -312,12 +311,11 @@
? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
contentDescription)
: null;
+ final String slot = getStatusBarIconSlotName(componentName);
mMainHandler.post(new Runnable() {
@Override
public void run() {
- StatusBarIconController iconController = mStatusBarIconController;
- iconController.setIcon(componentName.getClassName(), statusIcon);
- iconController.setExternalIcon(componentName.getClassName());
+ mStatusBarIconController.setIconFromTile(slot, statusIcon);
}
});
}
@@ -377,6 +375,12 @@
mCommandQueue.removeCallback(mRequestListeningCallback);
}
+ /** Returns the slot name that should be used when adding or removing status bar icons. */
+ private String getStatusBarIconSlotName(ComponentName componentName) {
+ return componentName.getClassName();
+ }
+
+
private final CommandQueue.Callbacks mRequestListeningCallback = new CommandQueue.Callbacks() {
@Override
public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 23c41db..5b461a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -16,8 +16,12 @@
package com.android.systemui.qs.logging
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.content.res.Configuration.Orientation
import android.service.quicksettings.Tile
import android.view.View
+import com.android.systemui.log.dagger.QSConfigLog
import com.android.systemui.log.dagger.QSLog
import com.android.systemui.plugins.log.ConstantStringsLogger
import com.android.systemui.plugins.log.ConstantStringsLoggerImpl
@@ -32,8 +36,12 @@
private const val TAG = "QSLog"
-class QSLogger @Inject constructor(@QSLog private val buffer: LogBuffer) :
- ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) {
+class QSLogger
+@Inject
+constructor(
+ @QSLog private val buffer: LogBuffer,
+ @QSConfigLog private val configChangedBuffer: LogBuffer,
+) : ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) {
fun logException(@CompileTimeConstant logMsg: String, ex: Exception) {
buffer.log(TAG, ERROR, {}, { logMsg }, exception = ex)
@@ -264,19 +272,28 @@
}
fun logOnConfigurationChanged(
- lastOrientation: Int,
- newOrientation: Int,
+ @Orientation oldOrientation: Int,
+ @Orientation newOrientation: Int,
+ newShouldUseSplitShade: Boolean,
+ oldShouldUseSplitShade: Boolean,
containerName: String
) {
- buffer.log(
+ configChangedBuffer.log(
TAG,
DEBUG,
{
str1 = containerName
- int1 = lastOrientation
+ int1 = oldOrientation
int2 = newOrientation
+ bool1 = oldShouldUseSplitShade
+ bool2 = newShouldUseSplitShade
},
- { "configuration change: $str1 orientation was $int1, now $int2" }
+ {
+ "config change: " +
+ "$str1 orientation=${toOrientationString(int2)} " +
+ "(was ${toOrientationString(int1)}), " +
+ "splitShade=$bool2 (was $bool1)"
+ }
)
}
@@ -353,3 +370,11 @@
}
}
}
+
+private inline fun toOrientationString(@Orientation orientation: Int): String {
+ return when (orientation) {
+ ORIENTATION_LANDSCAPE -> "land"
+ ORIENTATION_PORTRAIT -> "port"
+ else -> "undefined"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 557e95c..7ad594e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -115,6 +115,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -613,11 +615,13 @@
// Note that this may block if the sound is still being loaded (very unlikely) but we can't
// reliably release in the background because the service is being destroyed.
try {
- MediaPlayer player = mCameraSound.get();
+ MediaPlayer player = mCameraSound.get(1, TimeUnit.SECONDS);
if (player != null) {
player.release();
}
- } catch (InterruptedException | ExecutionException e) {
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ mCameraSound.cancel(true);
+ Log.w(TAG, "Error releasing shutter sound", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
index 48aa60f..253f07d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
@@ -17,6 +17,8 @@
package com.android.systemui.screenshot
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
+import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
import android.view.Display
import android.view.IWindowManager
import android.view.ViewGroup
@@ -45,7 +47,7 @@
// Convert component names to app names.
return components.map {
packageManager
- .getActivityInfo(it, PackageManager.ComponentInfoFlags.of(0))
+ .getActivityInfo(it, ComponentInfoFlags.of(MATCH_DISABLED_COMPONENTS.toLong()))
.loadLabel(packageManager)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
index 236213c..798c490 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable
import android.os.UserHandle
import android.os.UserManager
@@ -53,12 +54,9 @@
var badgedIcon: Drawable? = null
var label: CharSequence? = null
val fileManager = fileManagerComponentName()
+ ?: return WorkProfileFirstRunData(defaultFileAppName(), null)
try {
- val info =
- packageManager.getActivityInfo(
- fileManager,
- PackageManager.ComponentInfoFlags.of(0)
- )
+ val info = packageManager.getActivityInfo(fileManager, ComponentInfoFlags.of(0L))
val icon = packageManager.getActivityIcon(fileManager)
badgedIcon = packageManager.getUserBadgedIcon(icon, userHandle)
label = info.loadLabel(packageManager)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7dcfa0a..3360511 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -159,6 +159,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
import com.android.systemui.plugins.qs.QS;
@@ -1592,10 +1593,9 @@
transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- boolean customClockAnimation =
- mKeyguardStatusViewController.getClockAnimations() != null
- && mKeyguardStatusViewController.getClockAnimations()
- .getHasCustomPositionUpdatedAnimation();
+ ClockAnimations clockAnims = mKeyguardStatusViewController.getClockAnimations();
+ boolean customClockAnimation = clockAnims != null
+ && clockAnims.getHasCustomPositionUpdatedAnimation();
if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
// Find the clock, so we can exclude it from this transition.
@@ -2897,15 +2897,7 @@
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
mNotificationStackScrollLayoutController.getHeadsUpCallback(),
- NotificationPanelViewController.this);
- }
-
- public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
- if (pickedChild != null) {
- updateTrackingHeadsUp(pickedChild);
- mExpandingFromHeadsUp = true;
- }
- // otherwise we update the state when the expansion is finished
+ new HeadsUpNotificationViewControllerImpl());
}
private void onClosingFinished() {
@@ -2953,7 +2945,8 @@
}
/** Called when a HUN is dragged up or down to indicate the starting height for shade motion. */
- public void setHeadsUpDraggingStartingHeight(int startHeight) {
+ @VisibleForTesting
+ void setHeadsUpDraggingStartingHeight(int startHeight) {
mHeadsUpStartHeight = startHeight;
float scrimMinFraction;
if (mSplitShadeEnabled) {
@@ -2987,10 +2980,6 @@
mScrimController.setPanelScrimMinFraction(mMinFraction);
}
- public void clearNotificationEffects() {
- mCentralSurfaces.clearNotificationEffects();
- }
-
private boolean isPanelVisibleBecauseOfHeadsUp() {
return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
&& mBarState == StatusBarState.SHADE;
@@ -3629,7 +3618,13 @@
: (mKeyguardStateController.canDismissLockScreen()
? UNLOCK : BOUNCER_UNLOCK);
- fling(vel, expand, isFalseTouch(x, y, interactionType));
+ // don't fling while in keyguard to avoid jump in shade expand animation;
+ // touch has been intercepted already so flinging here is redundant
+ if (mBarState == KEYGUARD && mExpandedFraction >= 1.0) {
+ mShadeLog.d("NPVC endMotionEvent - skipping fling on keyguard");
+ } else {
+ fling(vel, expand, isFalseTouch(x, y, interactionType));
+ }
onTrackingStopped(expand);
mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
if (mUpdateFlingOnLayout) {
@@ -5124,9 +5119,14 @@
Rect from = (Rect) startValues.values.get(PROP_BOUNDS);
Rect to = (Rect) endValues.values.get(PROP_BOUNDS);
- anim.addUpdateListener(
- animation -> mController.getClockAnimations().onPositionUpdated(
- from, to, animation.getAnimatedFraction()));
+ anim.addUpdateListener(animation -> {
+ ClockAnimations clockAnims = mController.getClockAnimations();
+ if (clockAnims == null) {
+ return;
+ }
+
+ clockAnims.onPositionUpdated(from, to, animation.getAnimatedFraction());
+ });
return anim;
}
@@ -5137,6 +5137,33 @@
}
}
+ private final class HeadsUpNotificationViewControllerImpl implements
+ HeadsUpTouchHelper.HeadsUpNotificationViewController {
+ @Override
+ public void setHeadsUpDraggingStartingHeight(int startHeight) {
+ NotificationPanelViewController.this.setHeadsUpDraggingStartingHeight(startHeight);
+ }
+
+ @Override
+ public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+ if (pickedChild != null) {
+ updateTrackingHeadsUp(pickedChild);
+ mExpandingFromHeadsUp = true;
+ }
+ // otherwise we update the state when the expansion is finished
+ }
+
+ @Override
+ public void startExpand(float x, float y, boolean startTracking, float expandedHeight) {
+ startExpandMotion(x, y, startTracking, expandedHeight);
+ }
+
+ @Override
+ public void clearNotificationEffects() {
+ mCentralSurfaces.clearNotificationEffects();
+ }
+ }
+
private final class ShadeAccessibilityDelegate extends AccessibilityDelegate {
@Override
public void onInitializeAccessibilityNodeInfo(View host,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 5f6f158..0318fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -32,6 +32,8 @@
import android.view.ViewGroup;
import android.view.ViewStub;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
@@ -50,6 +52,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor;
+import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor;
import com.android.systemui.multishade.ui.view.MultiShadeView;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -118,6 +121,7 @@
step.getTransitionState() == TransitionState.RUNNING;
};
private final SystemClock mClock;
+ private final @Nullable MultiShadeMotionEventInteractor mMultiShadeMotionEventInteractor;
@Inject
public NotificationShadeWindowViewController(
@@ -145,7 +149,8 @@
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
FeatureFlags featureFlags,
Provider<MultiShadeInteractor> multiShadeInteractorProvider,
- SystemClock clock) {
+ SystemClock clock,
+ Provider<MultiShadeMotionEventInteractor> multiShadeMotionEventInteractorProvider) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -180,22 +185,18 @@
mClock = clock;
if (ComposeFacade.INSTANCE.isComposeAvailable()
&& featureFlags.isEnabled(Flags.DUAL_SHADE)) {
+ mMultiShadeMotionEventInteractor = multiShadeMotionEventInteractorProvider.get();
final ViewStub multiShadeViewStub = mView.findViewById(R.id.multi_shade_stub);
if (multiShadeViewStub != null) {
final MultiShadeView multiShadeView = (MultiShadeView) multiShadeViewStub.inflate();
multiShadeView.init(multiShadeInteractorProvider.get(), clock);
}
+ } else {
+ mMultiShadeMotionEventInteractor = null;
}
}
/**
- * @return Location where to place the KeyguardBouncer
- */
- public ViewGroup getBouncerContainer() {
- return mView.findViewById(R.id.keyguard_bouncer_container);
- }
-
- /**
* @return Location where to place the KeyguardMessageArea
*/
public AuthKeyguardMessageArea getKeyguardMessageArea() {
@@ -349,16 +350,17 @@
return true;
}
- boolean intercept = false;
- if (mNotificationPanelViewController.isFullyExpanded()
+ if (mMultiShadeMotionEventInteractor != null) {
+ // This interactor is not null only if the dual shade feature is enabled.
+ return mMultiShadeMotionEventInteractor.shouldIntercept(ev);
+ } else if (mNotificationPanelViewController.isFullyExpanded()
&& mDragDownHelper.isDragDownEnabled()
&& !mService.isBouncerShowing()
&& !mStatusBarStateController.isDozing()) {
- intercept = mDragDownHelper.onInterceptTouchEvent(ev);
+ return mDragDownHelper.onInterceptTouchEvent(ev);
+ } else {
+ return false;
}
-
- return intercept;
-
}
@Override
@@ -381,13 +383,20 @@
return true;
}
- if ((mDragDownHelper.isDragDownEnabled() && !handled)
- || mDragDownHelper.isDraggingDown()) {
- // we still want to finish our drag down gesture when locking the screen
- handled = mDragDownHelper.onTouchEvent(ev);
+ if (handled) {
+ return true;
}
- return handled;
+ if (mMultiShadeMotionEventInteractor != null) {
+ // This interactor is not null only if the dual shade feature is enabled.
+ return mMultiShadeMotionEventInteractor.onTouchEvent(ev, mView.getWidth());
+ } else if (mDragDownHelper.isDragDownEnabled()
+ || mDragDownHelper.isDraggingDown()) {
+ // we still want to finish our drag down gesture when locking the screen
+ return mDragDownHelper.onTouchEvent(ev);
+ } else {
+ return false;
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 55fa479..7f8c135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -217,8 +217,6 @@
NotificationPanelViewController getNotificationPanelViewController();
- ViewGroup getBouncerContainer();
-
/** Get the Keyguard Message Area that displays auth messages. */
AuthKeyguardMessageArea getKeyguardMessageArea();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 2771ec9..f105d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1725,11 +1725,6 @@
}
@Override
- public ViewGroup getBouncerContainer() {
- return mNotificationShadeWindowViewController.getBouncerContainer();
- }
-
- @Override
public AuthKeyguardMessageArea getKeyguardMessageArea() {
return mNotificationShadeWindowViewController.getKeyguardMessageArea();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 90d0b69..16c2e36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,7 +21,6 @@
import android.view.ViewConfiguration;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -31,21 +30,21 @@
*/
public class HeadsUpTouchHelper implements Gefingerpoken {
- private HeadsUpManagerPhone mHeadsUpManager;
- private Callback mCallback;
+ private final HeadsUpManagerPhone mHeadsUpManager;
+ private final Callback mCallback;
private int mTrackingPointer;
- private float mTouchSlop;
+ private final float mTouchSlop;
private float mInitialTouchX;
private float mInitialTouchY;
private boolean mTouchingHeadsUpView;
private boolean mTrackingHeadsUp;
private boolean mCollapseSnoozes;
- private NotificationPanelViewController mPanel;
+ private final HeadsUpNotificationViewController mPanel;
private ExpandableNotificationRow mPickedChild;
public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
Callback callback,
- NotificationPanelViewController notificationPanelView) {
+ HeadsUpNotificationViewController notificationPanelView) {
mHeadsUpManager = headsUpManager;
mCallback = callback;
mPanel = notificationPanelView;
@@ -116,7 +115,7 @@
int startHeight = (int) (mPickedChild.getActualHeight()
+ mPickedChild.getTranslationY());
mPanel.setHeadsUpDraggingStartingHeight(startHeight);
- mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
+ mPanel.startExpand(x, y, true /* startTracking */, startHeight);
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
mHeadsUpManager.unpinAll(true);
@@ -181,4 +180,19 @@
boolean isExpanded();
Context getContext();
}
+
+ /** The controller for a view that houses heads up notifications. */
+ public interface HeadsUpNotificationViewController {
+ /** Called when a HUN is dragged to indicate the starting height for shade motion. */
+ void setHeadsUpDraggingStartingHeight(int startHeight);
+
+ /** Sets notification that is being expanded. */
+ void setTrackedHeadsUp(ExpandableNotificationRow expandableNotificationRow);
+
+ /** Called when a MotionEvent is about to trigger expansion. */
+ void startExpand(float newX, float newY, boolean startTracking, float expandedHeight);
+
+ /** Clear any effects that were added for the expansion. */
+ void clearNotificationEffects();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 04cc8ce..30d2295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -81,28 +81,22 @@
void refreshIconGroup(IconManager iconManager);
/**
- * Adds or updates an icon for a given slot for a **tile service icon**.
+ * Adds or updates an icon that comes from an active tile service.
*
- * TODO(b/265307726): Merge with {@link #setIcon(String, StatusBarIcon)} or make this method
- * much more clearly distinct from that method.
+ * If the icon is null, the icon will be removed.
*/
- void setExternalIcon(String slot);
+ void setIconFromTile(String slot, @Nullable StatusBarIcon icon);
+
+ /** Removes an icon that had come from an active tile service. */
+ void removeIconForTile(String slot);
/**
* Adds or updates an icon for the given slot for **internal system icons**.
*
- * TODO(b/265307726): Rename to `setInternalIcon`, or merge this appropriately with the
- * {@link #setIcon(String, StatusBarIcon)} method.
+ * TODO(b/265307726): Re-name this to `setInternalIcon`.
*/
void setIcon(String slot, int resourceId, CharSequence contentDescription);
- /**
- * Adds or updates an icon for the given slot for an **externally-provided icon**.
- *
- * TODO(b/265307726): Rename to `setExternalIcon` or something similar.
- */
- void setIcon(String slot, StatusBarIcon icon);
-
/** */
void setWifiIcon(String slot, WifiIconState state);
@@ -152,15 +146,10 @@
*/
void removeIcon(String slot, int tag);
- /** */
- void removeAllIconsForSlot(String slot);
-
/**
- * Removes all the icons for the given slot.
- *
- * Only use this for icons that have come from **an external process**.
+ * TODO(b/265307726): Re-name this to `removeAllIconsForInternalSlot`.
*/
- void removeAllIconsForExternalSlot(String slot);
+ void removeAllIconsForSlot(String slot);
// TODO: See if we can rename this tunable name.
String ICON_HIDE_LIST = "icon_blacklist";
@@ -618,13 +607,6 @@
mGroup.removeAllViews();
}
- protected void onIconExternal(int viewIndex, int height) {
- ImageView imageView = (ImageView) mGroup.getChildAt(viewIndex);
- imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
- imageView.setAdjustViewBounds(true);
- setHeightAndCenter(imageView, height);
- }
-
protected void onDensityOrFontScaleChanged() {
for (int i = 0; i < mGroup.getChildCount(); i++) {
View child = mGroup.getChildAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 0727c5a..3a18423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -32,7 +32,6 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
@@ -62,7 +61,7 @@
*/
@SysUISingleton
public class StatusBarIconControllerImpl implements Tunable,
- ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode {
+ ConfigurationListener, Dumpable, StatusBarIconController, DemoMode {
private static final String TAG = "StatusBarIconController";
// Use this suffix to prevent external icon slot names from unintentionally overriding our
@@ -93,7 +92,7 @@
mStatusBarPipelineFlags = statusBarPipelineFlags;
configurationController.addCallback(this);
- commandQueue.addCallback(this);
+ commandQueue.addCallback(mCommandQueueCallbacks);
tunerService.addTunable(this, ICON_HIDE_LIST);
demoModeController.addCallback(this);
dumpManager.registerDumpable(getClass().getSimpleName(), this);
@@ -350,26 +349,35 @@
}
}
- @Override
- public void setExternalIcon(String slot) {
- String slotName = createExternalSlotName(slot);
- int viewIndex = mStatusBarIconList.getViewIndex(slotName, 0);
- int height = mContext.getResources().getDimensionPixelSize(
- R.dimen.status_bar_icon_drawing_size);
- mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
- }
-
- // Override for *both* CommandQueue.Callbacks AND StatusBarIconController.
- // TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
- // differentiate between those callback methods and StatusBarIconController methods.
- @Override
- public void setIcon(String slot, StatusBarIcon icon) {
- String slotName = createExternalSlotName(slot);
- if (icon == null) {
- removeAllIconsForSlot(slotName);
- return;
+ private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
+ @Override
+ public void setIcon(String slot, StatusBarIcon icon) {
+ // Icons that come from CommandQueue are from external services.
+ setExternalIcon(slot, icon);
}
+ @Override
+ public void removeIcon(String slot) {
+ removeAllIconsForExternalSlot(slot);
+ }
+ };
+
+ @Override
+ public void setIconFromTile(String slot, StatusBarIcon icon) {
+ setExternalIcon(slot, icon);
+ }
+
+ @Override
+ public void removeIconForTile(String slot) {
+ removeAllIconsForExternalSlot(slot);
+ }
+
+ private void setExternalIcon(String slot, StatusBarIcon icon) {
+ if (icon == null) {
+ removeAllIconsForExternalSlot(slot);
+ return;
+ }
+ String slotName = createExternalSlotName(slot);
StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
setIcon(slotName, holder);
}
@@ -417,14 +425,6 @@
}
}
- // CommandQueue.Callbacks override
- // TODO(b/265307726): Pull out the CommandQueue callbacks into a member variable to
- // differentiate between those callback methods and StatusBarIconController methods.
- @Override
- public void removeIcon(String slot) {
- removeAllIconsForExternalSlot(slot);
- }
-
/** */
@Override
public void removeIcon(String slot, int tag) {
@@ -444,8 +444,7 @@
mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
}
- @Override
- public void removeAllIconsForExternalSlot(String slotName) {
+ private void removeAllIconsForExternalSlot(String slotName) {
removeAllIconsForSlot(createExternalSlotName(slotName));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 06d0758..f06b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -381,7 +381,6 @@
mCentralSurfaces = centralSurfaces;
mBiometricUnlockController = biometricUnlockController;
- ViewGroup container = mCentralSurfaces.getBouncerContainer();
mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
if (shadeExpansionStateManager != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index ed978c3..24ddded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -453,9 +453,6 @@
protected int adjustDisableFlags(int state) {
boolean headsUpVisible =
mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
- if (headsUpVisible) {
- state |= DISABLE_CLOCK;
- }
if (!mKeyguardStateController.isLaunchTransitionFadingAway()
&& !mKeyguardStateController.isKeyguardFadingAway()
@@ -473,6 +470,13 @@
state |= DISABLE_ONGOING_CALL_CHIP;
}
+ if (headsUpVisible) {
+ // Disable everything on the left side of the status bar, since the app name for the
+ // heads up notification appears there instead.
+ state |= DISABLE_CLOCK;
+ state |= DISABLE_ONGOING_CALL_CHIP;
+ }
+
return state;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 4156fc1..73bf188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -179,6 +179,10 @@
fun logDefaultMobileIconGroup(group: SignalIcon.MobileIconGroup) {
buffer.log(TAG, LogLevel.INFO, { str1 = group.name }, { "defaultMobileIconGroup: $str1" })
}
+
+ fun logOnSubscriptionsChanged() {
+ buffer.log(TAG, LogLevel.INFO, {}, { "onSubscriptionsChanged" })
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index f866d65..d0c6215 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -105,10 +105,14 @@
* The reason we need to do this is because TelephonyManager limits the number of registered
* listeners per-process, so we don't want to create a new listener for every callback.
*
- * A note on the design for back pressure here: We use the [coalesce] operator here to change
- * the backpressure strategy to store exactly the last callback event of _each type_ here, as
- * opposed to the default strategy which is to drop the oldest event (regardless of type). This
- * means that we should never miss any single event as long as the flow has been started.
+ * A note on the design for back pressure here: We don't control _which_ telephony callback
+ * comes in first, since we register every relevant bit of information as a batch. E.g., if a
+ * downstream starts collecting on a field which is backed by
+ * [TelephonyCallback.ServiceStateListener], it's not possible for us to guarantee that _that_
+ * callback comes in -- the first callback could very well be
+ * [TelephonyCallback.DataActivityListener], which would promptly be dropped if we didn't keep
+ * it tracked. We use the [scan] operator here to track the most recent callback of _each type_
+ * here. See [TelephonyCallbackState] to see how the callbacks are stored.
*/
private val callbackEvents: StateFlow<TelephonyCallbackState> = run {
val initial = TelephonyCallbackState()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index b7da3f2..991b786 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -129,6 +129,7 @@
val callback =
object : SubscriptionManager.OnSubscriptionsChangedListener() {
override fun onSubscriptionsChanged() {
+ logger.logOnSubscriptionsChanged()
trySend(Unit)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 805368c..f1269f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -398,6 +398,7 @@
pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled);
pw.println(" isKeyguardFadingAway: " + isKeyguardFadingAway());
pw.println(" isKeyguardGoingAway: " + isKeyguardGoingAway());
+ pw.println(" isLaunchTransitionFadingAway: " + isLaunchTransitionFadingAway());
}
private class UpdateMonitorCallback extends KeyguardUpdateMonitorCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 125cc76..6e58f22 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -18,7 +18,8 @@
import android.os.VibrationEffect
import android.view.View
-import androidx.annotation.AttrRes
+import androidx.annotation.ColorRes
+import com.android.systemui.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.temporarydisplay.TemporaryViewInfo
@@ -48,7 +49,7 @@
override val priority: ViewPriority,
) : TemporaryViewInfo() {
companion object {
- @AttrRes const val DEFAULT_ICON_TINT_ATTR = android.R.attr.textColorPrimary
+ @ColorRes val DEFAULT_ICON_TINT = R.color.chipbar_text_and_icon_color
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a5f90f8..b15ac39 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@@ -365,6 +366,12 @@
assertEquals(View.VISIBLE, mFakeWeatherView.getVisibility());
}
+ @Test
+ public void testGetClockAnimations_nullClock_returnsNull() {
+ when(mClockEventController.getClock()).thenReturn(null);
+ assertNull(mController.getClockAnimations());
+ }
+
private void verifyAttachment(VerificationMode times) {
verify(mClockRegistry, times).registerClockChangeListener(
any(ClockRegistry.ClockChangeListener.class));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index f966eb3..b73330f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -196,6 +196,7 @@
.thenReturn(mKeyguardMessageAreaController);
when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
mKeyguardPasswordViewController = new KeyguardPasswordViewController(
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
SecurityMode.Password, mLockPatternUtils, null,
@@ -554,6 +555,22 @@
}
@Test
+ public void testSecurityCallbackFinish() {
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUserUnlocked(0)).thenReturn(true);
+ mKeyguardSecurityContainerController.finish(true, 0);
+ verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void testSecurityCallbackFinish_cannotDismissLockScreenAndNotStrongAuth() {
+ when(mFeatureFlags.isEnabled(Flags.PREVENT_BYPASS_KEYGUARD)).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+ mKeyguardSecurityContainerController.finish(false, 0);
+ verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
+ }
+
+ @Test
public void testOnStartingToHide() {
mKeyguardSecurityContainerController.onStartingToHide();
verify(mInputViewController).onStartingToHide();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 86ba30c..fb21db7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2256,6 +2256,26 @@
}
@Test
+ public void assistantVisible_requestActiveUnlock() {
+ // GIVEN active unlock requests from the assistant are allowed
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.ASSISTANT)).thenReturn(true);
+
+ // GIVEN should trigger active unlock
+ keyguardIsVisible();
+ keyguardNotGoingAway();
+ statusBarShadeIsNotLocked();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // WHEN the assistant is visible
+ mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+ // THEN request unlock with keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(true));
+ }
+
+ @Test
public void fingerprintFailure_requestActiveUnlock_dismissKeyguard()
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
@@ -2489,6 +2509,57 @@
}
@Test
+ public void unfoldFromPostureChange_requestActiveUnlock_forceDismissKeyguard()
+ throws RemoteException {
+ // GIVEN shouldTriggerActiveUnlock
+ keyguardIsVisible();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // GIVEN active unlock triggers on wakeup
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+ .thenReturn(true);
+
+ // GIVEN an unfold should force dismiss the keyguard
+ when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(true);
+
+ // WHEN device posture changes to unfold
+ deviceInPostureStateOpened();
+ mTestableLooper.processAllMessages();
+
+ // THEN request unlock with a keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(true));
+ }
+
+
+ @Test
+ public void unfoldFromPostureChange_requestActiveUnlock_noDismissKeyguard()
+ throws RemoteException {
+ // GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
+ keyguardIsVisible();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // GIVEN active unlock triggers on wakeup
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
+ .thenReturn(true);
+
+ // GIVEN an unfold should NOT force dismiss the keyguard
+ when(mActiveUnlockConfig.shouldWakeupForceDismissKeyguard(
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE)).thenReturn(false);
+
+ // WHEN device posture changes to unfold
+ deviceInPostureStateOpened();
+ mTestableLooper.processAllMessages();
+
+ // THEN request unlock WITHOUT a keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(false));
+ }
+
+ @Test
public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() {
ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 21191db..0ab675c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -62,6 +62,7 @@
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -98,7 +99,6 @@
@JvmField @Rule var rule = MockitoJUnit.rule()
- @Mock lateinit var keyguardStateController: KeyguardStateController
@Mock lateinit var layoutInflater: LayoutInflater
@Mock lateinit var fingerprintManager: FingerprintManager
@Mock lateinit var windowManager: WindowManager
@@ -138,7 +138,8 @@
keyguardBouncerRepository = FakeKeyguardBouncerRepository()
alternateBouncerInteractor =
AlternateBouncerInteractor(
- keyguardStateController,
+ mock(StatusBarStateController::class.java),
+ mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
FakeBiometricSettingsRepository(),
FakeDeviceEntryFingerprintAuthRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 786cb01..cefa9b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -93,6 +94,7 @@
)
mAlternateBouncerInteractor =
AlternateBouncerInteractor(
+ mock(StatusBarStateController::class.java),
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
mock(BiometricSettingsRepository::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 10757ae..5b3e518 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -201,6 +201,56 @@
}
@Test
+ fun testSingleAppHeaderIsNotClickable() {
+ mockLayoutInflater()
+ val packageName = "pkg"
+ `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
+ val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
+ val serviceInfo = setUpPanel(panel)
+
+ underTest.show(parent, {}, context)
+
+ val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
+
+ verify(controlsListingController).addCallback(capture(captor))
+
+ captor.value.onServicesUpdated(listOf(serviceInfo))
+ FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
+
+ val header: View = parent.requireViewById(R.id.controls_header)
+ assertThat(header.isClickable).isFalse()
+ assertThat(header.hasOnClickListeners()).isFalse()
+ }
+
+ @Test
+ fun testMultipleAppHeaderIsClickable() {
+ mockLayoutInflater()
+ val packageName1 = "pkg"
+ val panel1 = SelectedItem.PanelItem("App name 1", ComponentName(packageName1, "cls"))
+ val serviceInfo1 = setUpPanel(panel1)
+
+ val packageName2 = "pkg"
+ val panel2 = SelectedItem.PanelItem("App name 2", ComponentName(packageName2, "cls"))
+ val serviceInfo2 = setUpPanel(panel2)
+
+ `when`(authorizedPanelsRepository.getAuthorizedPanels())
+ .thenReturn(setOf(packageName1, packageName2))
+
+ underTest.show(parent, {}, context)
+
+ val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
+
+ verify(controlsListingController).addCallback(capture(captor))
+
+ captor.value.onServicesUpdated(listOf(serviceInfo1, serviceInfo2))
+ FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
+
+ val header: View = parent.requireViewById(R.id.controls_header)
+ assertThat(header.isClickable).isTrue()
+ assertThat(header.hasOnClickListeners()).isTrue()
+ }
+
+ @Test
fun testPanelControllerStartActivityWithCorrectArguments() {
mockLayoutInflater()
val packageName = "pkg"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
index 19347c7..58eb7d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -21,9 +21,11 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.DreamManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -50,6 +52,9 @@
@Mock
Condition.Callback mCallback;
+ @Mock
+ DreamManager mDreamManager;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -59,29 +64,39 @@
* Ensure a dreaming state immediately triggers the condition.
*/
@Test
- public void testInitialState() {
- final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
- when(mContext.registerReceiver(any(), any())).thenReturn(intent);
- final DreamCondition condition = new DreamCondition(mContext);
+ public void testInitialDreamingState() {
+ when(mDreamManager.isDreaming()).thenReturn(true);
+ final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
condition.addCallback(mCallback);
- condition.start();
verify(mCallback).onConditionChanged(eq(condition));
assertThat(condition.isConditionMet()).isTrue();
}
/**
+ * Ensure a non-dreaming state does not trigger the condition.
+ */
+ @Test
+ public void testInitialNonDreamingState() {
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
+ condition.addCallback(mCallback);
+
+ verify(mCallback, never()).onConditionChanged(eq(condition));
+ assertThat(condition.isConditionMet()).isFalse();
+ }
+
+ /**
* Ensure that changing dream state triggers condition.
*/
@Test
public void testChange() {
- final Intent intent = new Intent(Intent.ACTION_DREAMING_STARTED);
final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- when(mContext.registerReceiver(receiverCaptor.capture(), any())).thenReturn(intent);
- final DreamCondition condition = new DreamCondition(mContext);
+ when(mDreamManager.isDreaming()).thenReturn(true);
+ final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
condition.addCallback(mCallback);
- condition.start();
+ verify(mContext).registerReceiver(receiverCaptor.capture(), any());
clearInvocations(mCallback);
receiverCaptor.getValue().onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
verify(mCallback).onConditionChanged(eq(condition));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
index de0e511..2db4596 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
@@ -20,6 +20,7 @@
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -41,13 +42,14 @@
@Mock lateinit var statusBarStateController: StatusBarStateController
@Mock lateinit var powerManager: PowerManager
val clock = FakeSystemClock()
+ val executor = FakeExecutor(clock)
lateinit var listener: StatusBarStateController.StateListener
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
restartDozeListener =
- RestartDozeListener(settings, statusBarStateController, powerManager, clock)
+ RestartDozeListener(settings, statusBarStateController, powerManager, clock, executor)
val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
restartDozeListener.init()
@@ -58,12 +60,14 @@
@Test
fun testStoreDreamState_onDreamingStarted() {
listener.onDreamingChanged(true)
+ executor.runAllReady()
assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isTrue()
}
@Test
fun testStoreDreamState_onDreamingStopped() {
listener.onDreamingChanged(false)
+ executor.runAllReady()
assertThat(settings.getBool(RestartDozeListener.RESTART_NAP_KEY)).isFalse()
}
@@ -71,6 +75,8 @@
fun testRestoreDreamState_dreamingShouldStart() {
settings.putBool(RestartDozeListener.RESTART_NAP_KEY, true)
restartDozeListener.maybeRestartSleep()
+ executor.advanceClockToLast()
+ executor.runAllReady()
verify(powerManager).wakeUp(clock.uptimeMillis())
verify(powerManager).goToSleep(clock.uptimeMillis())
}
@@ -79,6 +85,8 @@
fun testRestoreDreamState_dreamingShouldNot() {
settings.putBool(RestartDozeListener.RESTART_NAP_KEY, false)
restartDozeListener.maybeRestartSleep()
+ executor.advanceClockToLast()
+ executor.runAllReady()
verify(powerManager, never()).wakeUp(anyLong())
verify(powerManager, never()).goToSleep(anyLong())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
index 1365132..86246f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
@@ -39,8 +40,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -52,6 +55,7 @@
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var deviceEntryFingerprintAuthRepository:
FakeDeviceEntryFingerprintAuthRepository
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -73,6 +77,7 @@
featureFlags = FakeFeatureFlags().apply { this.set(Flags.MODERN_ALTERNATE_BOUNCER, true) }
underTest =
AlternateBouncerInteractor(
+ statusBarStateController,
keyguardStateController,
bouncerRepository,
biometricSettingsRepository,
@@ -130,6 +135,14 @@
}
@Test
+ fun canShowAlternateBouncerForFingerprint_isDozing() {
+ givenCanShowAlternateBouncer()
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+
+ assertFalse(underTest.canShowAlternateBouncerForFingerprint())
+ }
+
+ @Test
fun show_whenCanShow() {
givenCanShowAlternateBouncer()
@@ -169,6 +182,42 @@
assertFalse(bouncerRepository.alternateBouncerVisible.value)
}
+ @Test
+ fun onUnlockedIsFalse_doesNotHide() {
+ // GIVEN alternate bouncer is showing
+ bouncerRepository.setAlternateVisible(true)
+
+ val keyguardStateControllerCallbackCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java)
+ verify(keyguardStateController).addCallback(keyguardStateControllerCallbackCaptor.capture())
+
+ // WHEN isUnlocked=false
+ givenCanShowAlternateBouncer()
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ keyguardStateControllerCallbackCaptor.value.onUnlockedChanged()
+
+ // THEN the alternate bouncer is still visible
+ assertTrue(bouncerRepository.alternateBouncerVisible.value)
+ }
+
+ @Test
+ fun onUnlockedChangedIsTrue_hide() {
+ // GIVEN alternate bouncer is showing
+ bouncerRepository.setAlternateVisible(true)
+
+ val keyguardStateControllerCallbackCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java)
+ verify(keyguardStateController).addCallback(keyguardStateControllerCallbackCaptor.capture())
+
+ // WHEN isUnlocked=true
+ givenCanShowAlternateBouncer()
+ whenever(keyguardStateController.isUnlocked).thenReturn(true)
+ keyguardStateControllerCallbackCaptor.value.onUnlockedChanged()
+
+ // THEN the alternate bouncer is hidden
+ assertFalse(bouncerRepository.alternateBouncerVisible.value)
+ }
+
private fun givenCanShowAlternateBouncer() {
bouncerRepository.setAlternateBouncerUIAvailable(true)
biometricSettingsRepository.setFingerprintEnrolled(true)
@@ -176,6 +225,7 @@
biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
deviceEntryFingerprintAuthRepository.setLockedOut(false)
whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ whenever(statusBarStateController.isDozing).thenReturn(false)
}
private fun givenCannotShowAlternateBouncer() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 85e8d07..6c3d6f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo.Companion.DEFAULT_ICON_TINT
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -140,6 +141,7 @@
context.getString(R.string.media_transfer_receiver_content_description_unknown_app)
)
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Resource(R.drawable.ic_cast))
+ assertThat(iconInfo.tint).isEqualTo(DEFAULT_ICON_TINT)
}
@Test
@@ -232,40 +234,40 @@
fun iconInfo_toTintedIcon_loaded() {
val contentDescription = ContentDescription.Loaded("test")
val drawable = context.getDrawable(R.drawable.ic_cake)!!
- val tintAttr = android.R.attr.textColorTertiary
+ val tint = R.color.GM2_blue_500
val iconInfo =
IconInfo(
contentDescription,
MediaTttIcon.Loaded(drawable),
- tintAttr,
+ tint,
isAppIcon = false,
)
val tinted = iconInfo.toTintedIcon()
assertThat(tinted.icon).isEqualTo(Icon.Loaded(drawable, contentDescription))
- assertThat(tinted.tintAttr).isEqualTo(tintAttr)
+ assertThat(tinted.tint).isEqualTo(tint)
}
@Test
fun iconInfo_toTintedIcon_resource() {
val contentDescription = ContentDescription.Loaded("test")
val drawableRes = R.drawable.ic_cake
- val tintAttr = android.R.attr.textColorTertiary
+ val tint = R.color.GM2_blue_500
val iconInfo =
IconInfo(
contentDescription,
MediaTttIcon.Resource(drawableRes),
- tintAttr,
+ tint,
isAppIcon = false
)
val tinted = iconInfo.toTintedIcon()
assertThat(tinted.icon).isEqualTo(Icon.Resource(drawableRes, contentDescription))
- assertThat(tinted.tintAttr).isEqualTo(tintAttr)
+ assertThat(tinted.tint).isEqualTo(tint)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt
index 415e68f..bcc99bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt
@@ -73,6 +73,28 @@
}
@Test
+ fun isAnyShadeExpanded() =
+ testScope.runTest {
+ val underTest = create()
+ val isAnyShadeExpanded: Boolean? by collectLastValue(underTest.isAnyShadeExpanded)
+ assertWithMessage("isAnyShadeExpanded must start with false!")
+ .that(isAnyShadeExpanded)
+ .isFalse()
+
+ underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0.441f)
+ assertThat(isAnyShadeExpanded).isTrue()
+
+ underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0.442f)
+ assertThat(isAnyShadeExpanded).isTrue()
+
+ underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0f)
+ assertThat(isAnyShadeExpanded).isTrue()
+
+ underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0f)
+ assertThat(isAnyShadeExpanded).isFalse()
+ }
+
+ @Test
fun isVisible_dualShadeConfig() =
testScope.runTest {
overrideResource(R.bool.dual_shade_enabled, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
new file mode 100644
index 0000000..f807146c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.multishade.domain.interactor
+
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
+import com.android.systemui.multishade.data.repository.MultiShadeRepository
+import com.android.systemui.multishade.shared.model.ProxiedInputModel
+import com.android.systemui.multishade.shared.model.ShadeId
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.currentTime
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class MultiShadeMotionEventInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: MultiShadeMotionEventInteractor
+
+ private lateinit var testScope: TestScope
+ private lateinit var motionEvents: MutableSet<MotionEvent>
+ private lateinit var repository: MultiShadeRepository
+ private lateinit var interactor: MultiShadeInteractor
+ private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
+
+ @Before
+ fun setUp() {
+ testScope = TestScope()
+ motionEvents = mutableSetOf()
+
+ val inputProxy = MultiShadeInputProxy()
+ repository =
+ MultiShadeRepository(
+ applicationContext = context,
+ inputProxy = inputProxy,
+ )
+ interactor =
+ MultiShadeInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository = repository,
+ inputProxy = inputProxy,
+ )
+ underTest =
+ MultiShadeMotionEventInteractor(
+ applicationContext = context,
+ applicationScope = testScope.backgroundScope,
+ interactor = interactor,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ motionEvents.forEach { motionEvent -> motionEvent.recycle() }
+ }
+
+ @Test
+ fun shouldIntercept_initialDown_returnsFalse() =
+ testScope.runTest {
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))).isFalse()
+ }
+
+ @Test
+ fun shouldIntercept_moveBelowTouchSlop_returnsFalse() =
+ testScope.runTest {
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop - 1f,
+ )
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun shouldIntercept_moveAboveTouchSlop_returnsTrue() =
+ testScope.runTest {
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop + 1f,
+ )
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun shouldIntercept_moveAboveTouchSlop_butHorizontalFirst_returnsFalse() =
+ testScope.runTest {
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ x = touchSlop + 1f,
+ )
+ )
+ )
+ .isFalse()
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop + 1f,
+ )
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun shouldIntercept_up_afterMovedAboveTouchSlop_returnsTrue() =
+ testScope.runTest {
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop + 1f))
+
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isTrue()
+ }
+
+ @Test
+ fun shouldIntercept_cancel_afterMovedAboveTouchSlop_returnsTrue() =
+ testScope.runTest {
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop + 1f))
+
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isTrue()
+ }
+
+ @Test
+ fun shouldIntercept_moveAboveTouchSlopAndUp_butShadeExpanded_returnsFalse() =
+ testScope.runTest {
+ repository.setExpansion(ShadeId.LEFT, 0.1f)
+ runCurrent()
+
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop + 1f,
+ )
+ )
+ )
+ .isFalse()
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isFalse()
+ }
+
+ @Test
+ fun shouldIntercept_moveAboveTouchSlopAndCancel_butShadeExpanded_returnsFalse() =
+ testScope.runTest {
+ repository.setExpansion(ShadeId.LEFT, 0.1f)
+ runCurrent()
+
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+
+ assertThat(
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ y = touchSlop + 1f,
+ )
+ )
+ )
+ .isFalse()
+ assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isFalse()
+ }
+
+ @Test
+ fun tap_doesNotSendProxiedInput() =
+ testScope.runTest {
+ val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+ val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+ val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))
+
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+ }
+
+ @Test
+ fun dragBelowTouchSlop_doesNotSendProxiedInput() =
+ testScope.runTest {
+ val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+ val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+ val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop - 1f))
+ underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))
+
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+ }
+
+ @Test
+ fun dragAboveTouchSlopAndUp() =
+ testScope.runTest {
+ val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+ val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+ val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_DOWN,
+ x = 100f, // left shade
+ )
+ )
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val yDragAmountPx = touchSlop + 1f
+ val moveEvent =
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ x = 100f, // left shade
+ y = yDragAmountPx,
+ )
+ assertThat(underTest.shouldIntercept(moveEvent)).isTrue()
+ underTest.onTouchEvent(moveEvent, viewWidthPx = 1000)
+ assertThat(leftShadeProxiedInput)
+ .isEqualTo(
+ ProxiedInputModel.OnDrag(
+ xFraction = 0.1f,
+ yDragAmountPx = yDragAmountPx,
+ )
+ )
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val upEvent = motionEvent(MotionEvent.ACTION_UP)
+ assertThat(underTest.shouldIntercept(upEvent)).isTrue()
+ underTest.onTouchEvent(upEvent, viewWidthPx = 1000)
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+ }
+
+ @Test
+ fun dragAboveTouchSlopAndCancel() =
+ testScope.runTest {
+ val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT))
+ val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT))
+ val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE))
+
+ underTest.shouldIntercept(
+ motionEvent(
+ MotionEvent.ACTION_DOWN,
+ x = 900f, // right shade
+ )
+ )
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val yDragAmountPx = touchSlop + 1f
+ val moveEvent =
+ motionEvent(
+ MotionEvent.ACTION_MOVE,
+ x = 900f, // right shade
+ y = yDragAmountPx,
+ )
+ assertThat(underTest.shouldIntercept(moveEvent)).isTrue()
+ underTest.onTouchEvent(moveEvent, viewWidthPx = 1000)
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput)
+ .isEqualTo(
+ ProxiedInputModel.OnDrag(
+ xFraction = 0.9f,
+ yDragAmountPx = yDragAmountPx,
+ )
+ )
+ assertThat(singleShadeProxiedInput).isNull()
+
+ val cancelEvent = motionEvent(MotionEvent.ACTION_CANCEL)
+ assertThat(underTest.shouldIntercept(cancelEvent)).isTrue()
+ underTest.onTouchEvent(cancelEvent, viewWidthPx = 1000)
+ assertThat(leftShadeProxiedInput).isNull()
+ assertThat(rightShadeProxiedInput).isNull()
+ assertThat(singleShadeProxiedInput).isNull()
+ }
+
+ private fun TestScope.motionEvent(
+ action: Int,
+ downTime: Long = currentTime,
+ eventTime: Long = currentTime,
+ x: Float = 0f,
+ y: Float = 0f,
+ ): MotionEvent {
+ val motionEvent = MotionEvent.obtain(downTime, eventTime, action, x, y, 0)
+ motionEvents.add(motionEvent)
+ return motionEvent
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt
new file mode 100644
index 0000000..8935309
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.multishade.shared.math
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class MathTest : SysuiTestCase() {
+
+ @Test
+ fun isZero_zero_true() {
+ assertThat(0f.isZero(epsilon = EPSILON)).isTrue()
+ }
+
+ @Test
+ fun isZero_belowPositiveEpsilon_true() {
+ assertThat((EPSILON * 0.999999f).isZero(epsilon = EPSILON)).isTrue()
+ }
+
+ @Test
+ fun isZero_aboveNegativeEpsilon_true() {
+ assertThat((EPSILON * -0.999999f).isZero(epsilon = EPSILON)).isTrue()
+ }
+
+ @Test
+ fun isZero_positiveEpsilon_false() {
+ assertThat(EPSILON.isZero(epsilon = EPSILON)).isFalse()
+ }
+
+ @Test
+ fun isZero_negativeEpsilon_false() {
+ assertThat((-EPSILON).isZero(epsilon = EPSILON)).isFalse()
+ }
+
+ @Test
+ fun isZero_positive_false() {
+ assertThat(1f.isZero(epsilon = EPSILON)).isFalse()
+ }
+
+ @Test
+ fun isZero_negative_false() {
+ assertThat((-1f).isZero(epsilon = EPSILON)).isFalse()
+ }
+
+ companion object {
+ private const val EPSILON = 0.0001f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 0a8cd26..0ee52ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -46,6 +46,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.isNull
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
@@ -267,7 +268,8 @@
verifyZeroInteractions(context)
val intentCaptor = argumentCaptor<Intent>()
- verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle))
+ verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
+ isNull())
intentCaptor.value.let { intent ->
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
@@ -401,7 +403,8 @@
createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
val intentCaptor = argumentCaptor<Intent>()
- verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle))
+ verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
+ isNull())
intentCaptor.value.let { intent ->
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
@@ -424,7 +427,8 @@
createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
val intentCaptor = argumentCaptor<Intent>()
- verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle))
+ verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
+ isNull())
intentCaptor.value.let { intent ->
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index 1f18d91..08b5d2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -19,12 +19,14 @@
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.IWindowManager
import android.view.WindowManager
import androidx.test.filters.SmallTest
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import junit.framework.Assert.assertEquals
@@ -32,6 +34,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -158,4 +161,56 @@
assertEquals(appName2, list[1])
assertEquals(appName3, list[2])
}
+
+ private fun includesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
+ ComponentInfoFlagMatcher(mask, mask)
+ private fun excludesFlagBits(@PackageManager.ComponentInfoFlagsBits mask: Int) =
+ ComponentInfoFlagMatcher(mask, 0)
+
+ private class ComponentInfoFlagMatcher(
+ @PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
+ ): ArgumentMatcher<PackageManager.ComponentInfoFlags> {
+ override fun matches(flags: PackageManager.ComponentInfoFlags): Boolean {
+ return (mask.toLong() and flags.value) == value.toLong()
+ }
+
+ override fun toString(): String{
+ return "mask 0x%08x == 0x%08x".format(mask, value)
+ }
+ }
+
+ @Test
+ fun testMaybeNotifyOfScreenshot_disabledApp() {
+ val data = ScreenshotData.forTesting()
+ data.source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+
+ val component = ComponentName("package1", "class1")
+ val appName = "app name"
+ val activityInfo = mock(ActivityInfo::class.java)
+
+ whenever(
+ packageManager.getActivityInfo(
+ eq(component),
+ argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS))
+ )
+ ).thenReturn(activityInfo);
+
+ whenever(
+ packageManager.getActivityInfo(
+ eq(component),
+ argThat(excludesFlagBits(MATCH_DISABLED_COMPONENTS))
+ )
+ ).thenThrow(PackageManager.NameNotFoundException::class.java);
+
+ whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
+ .thenReturn(listOf(component))
+
+ whenever(activityInfo.loadLabel(eq(packageManager))).thenReturn(appName)
+
+ val list = controller.maybeNotifyOfScreenshot(data)
+
+ assertEquals(1, list.size)
+ assertEquals(appName, list[0])
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
index 3440f91..31f7771 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
@@ -45,7 +45,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -58,8 +57,9 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class WorkProfileMessageControllerTest extends SysuiTestCase {
- private static final String DEFAULT_LABEL = "default label";
- private static final String APP_LABEL = "app label";
+ private static final String FILES_APP_COMPONENT = "com.android.test/.FilesComponent";
+ private static final String FILES_APP_LABEL = "Custom Files App";
+ private static final String DEFAULT_FILES_APP_LABEL = "Files";
private static final UserHandle NON_WORK_USER = UserHandle.of(0);
private static final UserHandle WORK_USER = UserHandle.of(10);
@@ -88,14 +88,21 @@
when(mMockContext.getSharedPreferences(
eq(WorkProfileMessageController.SHARED_PREFERENCES_NAME),
eq(Context.MODE_PRIVATE))).thenReturn(mSharedPreferences);
- when(mMockContext.getString(ArgumentMatchers.anyInt())).thenReturn(DEFAULT_LABEL);
- when(mPackageManager.getActivityIcon(any(ComponentName.class)))
+ when(mMockContext.getString(R.string.config_sceenshotWorkProfileFilesApp))
+ .thenReturn(FILES_APP_COMPONENT);
+ when(mMockContext.getString(R.string.screenshot_default_files_app_name))
+ .thenReturn(DEFAULT_FILES_APP_LABEL);
+ when(mPackageManager.getActivityIcon(
+ eq(ComponentName.unflattenFromString(FILES_APP_COMPONENT))))
.thenReturn(mActivityIcon);
- when(mPackageManager.getUserBadgedIcon(
- any(), any())).thenReturn(mBadgedActivityIcon);
- when(mPackageManager.getActivityInfo(any(),
- any(PackageManager.ComponentInfoFlags.class))).thenReturn(mActivityInfo);
- when(mActivityInfo.loadLabel(eq(mPackageManager))).thenReturn(APP_LABEL);
+ when(mPackageManager.getUserBadgedIcon(any(), any()))
+ .thenReturn(mBadgedActivityIcon);
+ when(mPackageManager.getActivityInfo(
+ eq(ComponentName.unflattenFromString(FILES_APP_COMPONENT)),
+ any(PackageManager.ComponentInfoFlags.class)))
+ .thenReturn(mActivityInfo);
+ when(mActivityInfo.loadLabel(eq(mPackageManager)))
+ .thenReturn(FILES_APP_LABEL);
mSharedPreferences.edit().putBoolean(
WorkProfileMessageController.PREFERENCE_KEY, false).apply();
@@ -120,14 +127,15 @@
@Test
public void testOnScreenshotTaken_packageNotFound()
throws PackageManager.NameNotFoundException {
- when(mPackageManager.getActivityInfo(any(),
+ when(mPackageManager.getActivityInfo(
+ eq(ComponentName.unflattenFromString(FILES_APP_COMPONENT)),
any(PackageManager.ComponentInfoFlags.class))).thenThrow(
new PackageManager.NameNotFoundException());
WorkProfileMessageController.WorkProfileFirstRunData data =
mMessageController.onScreenshotTaken(WORK_USER);
- assertEquals(DEFAULT_LABEL, data.getAppName());
+ assertEquals(DEFAULT_FILES_APP_LABEL, data.getAppName());
assertNull(data.getIcon());
}
@@ -136,16 +144,28 @@
WorkProfileMessageController.WorkProfileFirstRunData data =
mMessageController.onScreenshotTaken(WORK_USER);
- assertEquals(APP_LABEL, data.getAppName());
+ assertEquals(FILES_APP_LABEL, data.getAppName());
assertEquals(mBadgedActivityIcon, data.getIcon());
}
@Test
+ public void testOnScreenshotTaken_noFilesAppComponentDefined() {
+ when(mMockContext.getString(R.string.config_sceenshotWorkProfileFilesApp))
+ .thenReturn("");
+
+ WorkProfileMessageController.WorkProfileFirstRunData data =
+ mMessageController.onScreenshotTaken(WORK_USER);
+
+ assertEquals(DEFAULT_FILES_APP_LABEL, data.getAppName());
+ assertNull(data.getIcon());
+ }
+
+ @Test
public void testPopulateView() throws InterruptedException {
ViewGroup layout = (ViewGroup) LayoutInflater.from(mContext).inflate(
R.layout.screenshot_work_profile_first_run, null);
WorkProfileMessageController.WorkProfileFirstRunData data =
- new WorkProfileMessageController.WorkProfileFirstRunData(APP_LABEL,
+ new WorkProfileMessageController.WorkProfileFirstRunData(FILES_APP_LABEL,
mBadgedActivityIcon);
final CountDownLatch countdown = new CountDownLatch(1);
mMessageController.populateView(layout, data, () -> {
@@ -157,7 +177,7 @@
assertEquals(mBadgedActivityIcon, image.getDrawable());
TextView text = layout.findViewById(R.id.screenshot_message_content);
// The app name is used in a template, but at least validate that it was inserted.
- assertTrue(text.getText().toString().contains(APP_LABEL));
+ assertTrue(text.getText().toString().contains(FILES_APP_LABEL));
// Validate that clicking the dismiss button calls back properly.
assertEquals(1, countdown.getCount());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 629208e..5f34b2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
import com.android.systemui.multishade.data.repository.MultiShadeRepository
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
+import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
@@ -62,7 +63,6 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -129,6 +129,16 @@
val inputProxy = MultiShadeInputProxy()
testScope = TestScope()
+ val multiShadeInteractor =
+ MultiShadeInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository =
+ MultiShadeRepository(
+ applicationContext = context,
+ inputProxy = inputProxy,
+ ),
+ inputProxy = inputProxy,
+ )
underTest =
NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
@@ -154,18 +164,15 @@
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
+ { multiShadeInteractor },
+ FakeSystemClock(),
{
- MultiShadeInteractor(
+ MultiShadeMotionEventInteractor(
+ applicationContext = context,
applicationScope = testScope.backgroundScope,
- repository =
- MultiShadeRepository(
- applicationContext = context,
- inputProxy = inputProxy,
- ),
- inputProxy = inputProxy,
+ interactor = multiShadeInteractor,
)
},
- FakeSystemClock(),
)
underTest.setupExpandedStatusBar()
@@ -308,7 +315,7 @@
fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
// down event should be intercepted by keyguardViewManager
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(true)
+ .thenReturn(true)
// Then touch should not be intercepted
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
@@ -316,14 +323,6 @@
}
@Test
- fun testGetBouncerContainer() =
- testScope.runTest {
- Mockito.clearInvocations(view)
- underTest.bouncerContainer
- verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
- }
-
- @Test
fun testGetKeyguardMessageArea() =
testScope.runTest {
underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index b4b5ec1..b40181e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
import com.android.systemui.multishade.data.repository.MultiShadeRepository
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
+import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -140,6 +141,16 @@
featureFlags.set(Flags.DUAL_SHADE, false)
val inputProxy = MultiShadeInputProxy()
testScope = TestScope()
+ val multiShadeInteractor =
+ MultiShadeInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository =
+ MultiShadeRepository(
+ applicationContext = context,
+ inputProxy = inputProxy,
+ ),
+ inputProxy = inputProxy,
+ )
controller =
NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
@@ -165,18 +176,15 @@
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
+ { multiShadeInteractor },
+ FakeSystemClock(),
{
- MultiShadeInteractor(
+ MultiShadeMotionEventInteractor(
+ applicationContext = context,
applicationScope = testScope.backgroundScope,
- repository =
- MultiShadeRepository(
- applicationContext = context,
- inputProxy = inputProxy,
- ),
- inputProxy = inputProxy,
+ interactor = multiShadeInteractor,
)
},
- FakeSystemClock(),
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
index 3108ed9..fe12051 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
@@ -21,6 +21,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
+import android.view.View;
import androidx.test.filters.SmallTest;
@@ -49,6 +50,13 @@
}
@Test
+ public void userSwitcherChip_defaultVisibilityIsGone() {
+ assertThat(mKeyguardStatusBarView.findViewById(
+ R.id.user_switcher_container).getVisibility()).isEqualTo(
+ View.GONE);
+ }
+
+ @Test
public void setTopClipping_clippingUpdated() {
int topClipping = 40;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
index 3bc288a2..08e89fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
@@ -20,13 +20,17 @@
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl.EXTERNAL_SLOT_SUFFIX
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.Mock
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
@SmallTest
class StatusBarIconControllerImplTest : SysuiTestCase() {
@@ -34,15 +38,19 @@
private lateinit var underTest: StatusBarIconControllerImpl
private lateinit var iconList: StatusBarIconList
+ private lateinit var commandQueueCallbacks: CommandQueue.Callbacks
private val iconGroup: StatusBarIconController.IconManager = mock()
+ @Mock private lateinit var commandQueue: CommandQueue
+
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
iconList = StatusBarIconList(arrayOf())
underTest =
StatusBarIconControllerImpl(
context,
- mock(),
+ commandQueue,
mock(),
mock(),
mock(),
@@ -51,11 +59,14 @@
mock(),
)
underTest.addIconGroup(iconGroup)
+ val commandQueueCallbacksCaptor = kotlinArgumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(commandQueueCallbacksCaptor.capture())
+ commandQueueCallbacks = commandQueueCallbacksCaptor.value
}
/** Regression test for b/255428281. */
@Test
- fun internalAndExternalIconWithSameName_bothDisplayed() {
+ fun internalAndExternalIconWithSameName_externalFromTile_bothDisplayed() {
val slotName = "mute"
// Internal
@@ -71,7 +82,7 @@
/* number= */ 0,
"contentDescription",
)
- underTest.setIcon(slotName, externalIcon)
+ underTest.setIconFromTile(slotName, externalIcon)
assertThat(iconList.slots).hasSize(2)
// Whichever was added last comes first
@@ -83,17 +94,45 @@
/** Regression test for b/255428281. */
@Test
- fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveIcon_internalStays() {
+ fun internalAndExternalIconWithSameName_externalFromCommandQueue_bothDisplayed() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
- underTest.setIcon(slotName, createExternalIcon())
+ val externalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 2,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "contentDescription",
+ )
+ commandQueueCallbacks.setIcon(slotName, externalIcon)
- // WHEN the external icon is removed via #removeIcon
- underTest.removeIcon(slotName)
+ assertThat(iconList.slots).hasSize(2)
+ // Whichever was added last comes first
+ assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(iconList.slots[1].name).isEqualTo(slotName)
+ assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
+ assertThat(iconList.slots[1].hasIconsInSlot()).isTrue()
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_externalRemoved_fromCommandQueue_internalStays() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ commandQueueCallbacks.setIcon(slotName, createExternalIcon())
+
+ // WHEN the external icon is removed via CommandQueue.Callbacks#removeIcon
+ commandQueueCallbacks.removeIcon(slotName)
// THEN the external icon is removed but the internal icon remains
// Note: [StatusBarIconList] never removes slots from its list, it just sets the holder for
@@ -109,17 +148,17 @@
/** Regression test for b/255428281. */
@Test
- fun internalAndExternalIconWithSameName_externalRemoved_viaRemoveAll_internalStays() {
+ fun internalAndExternalIconWithSameName_externalRemoved_fromTileRemove_internalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
- underTest.setIcon(slotName, createExternalIcon())
+ underTest.setIconFromTile(slotName, createExternalIcon())
- // WHEN the external icon is removed via #removeAllIconsForExternalSlot
- underTest.removeAllIconsForExternalSlot(slotName)
+ // WHEN the external icon is removed via #removeIconForTile
+ underTest.removeIconForTile(slotName)
// THEN the external icon is removed but the internal icon remains
assertThat(iconList.slots).hasSize(2)
@@ -133,17 +172,17 @@
/** Regression test for b/255428281. */
@Test
- fun internalAndExternalIconWithSameName_externalRemoved_viaSetNull_internalStays() {
+ fun internalAndExternalIconWithSameName_externalRemoved_fromTileSetNull_internalStays() {
val slotName = "mute"
// Internal
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
- underTest.setIcon(slotName, createExternalIcon())
+ underTest.setIconFromTile(slotName, createExternalIcon())
- // WHEN the external icon is removed via a #setIcon(null)
- underTest.setIcon(slotName, /* icon= */ null)
+ // WHEN the external icon is removed via a #setIconFromTile(null)
+ underTest.setIconFromTile(slotName, /* icon= */ null)
// THEN the external icon is removed but the internal icon remains
assertThat(iconList.slots).hasSize(2)
@@ -164,12 +203,12 @@
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
- underTest.setIcon(slotName, createExternalIcon())
+ underTest.setIconFromTile(slotName, createExternalIcon())
// WHEN the internal icon is removed via #removeIcon
underTest.removeIcon(slotName, /* tag= */ 0)
- // THEN the external icon is removed but the internal icon remains
+ // THEN the internal icon is removed but the external icon remains
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
@@ -188,12 +227,12 @@
underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
// External
- underTest.setIcon(slotName, createExternalIcon())
+ underTest.setIconFromTile(slotName, createExternalIcon())
// WHEN the internal icon is removed via #removeAllIconsForSlot
underTest.removeAllIconsForSlot(slotName)
- // THEN the external icon is removed but the internal icon remains
+ // THEN the internal icon is removed but the external icon remains
assertThat(iconList.slots).hasSize(2)
assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
assertThat(iconList.slots[1].name).isEqualTo(slotName)
@@ -221,7 +260,7 @@
/* number= */ 0,
"externalDescription",
)
- underTest.setIcon(slotName, startingExternalIcon)
+ underTest.setIconFromTile(slotName, startingExternalIcon)
// WHEN the internal icon is updated
underTest.setIcon(slotName, /* resourceId= */ 11, "newContentDescription")
@@ -243,7 +282,7 @@
/** Regression test for b/255428281. */
@Test
- fun internalAndExternalIconWithSameName_externalUpdatedIndependently() {
+ fun internalAndExternalIconWithSameName_fromTile_externalUpdatedIndependently() {
val slotName = "mute"
// Internal
@@ -259,7 +298,7 @@
/* number= */ 0,
"externalDescription",
)
- underTest.setIcon(slotName, startingExternalIcon)
+ underTest.setIconFromTile(slotName, startingExternalIcon)
// WHEN the external icon is updated
val newExternalIcon =
@@ -271,7 +310,54 @@
/* number= */ 0,
"newExternalDescription",
)
- underTest.setIcon(slotName, newExternalIcon)
+ underTest.setIconFromTile(slotName, newExternalIcon)
+
+ // THEN only the external slot gets the updates
+ val externalSlot = iconList.slots[0]
+ val externalHolder = externalSlot.getHolderForTag(TAG_PRIMARY)!!
+ assertThat(externalSlot.name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
+ assertThat(externalHolder.icon!!.contentDescription).isEqualTo("newExternalDescription")
+ assertThat(externalHolder.icon!!.icon.resId).isEqualTo(21)
+
+ // And the internal slot has its own values
+ val internalSlot = iconList.slots[1]
+ val internalHolder = internalSlot.getHolderForTag(TAG_PRIMARY)!!
+ assertThat(internalSlot.name).isEqualTo(slotName)
+ assertThat(internalHolder.icon!!.contentDescription).isEqualTo("contentDescription")
+ assertThat(internalHolder.icon!!.icon.resId).isEqualTo(10)
+ }
+
+ /** Regression test for b/255428281. */
+ @Test
+ fun internalAndExternalIconWithSameName_fromCommandQueue_externalUpdatedIndependently() {
+ val slotName = "mute"
+
+ // Internal
+ underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
+
+ // External
+ val startingExternalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 20,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "externalDescription",
+ )
+ commandQueueCallbacks.setIcon(slotName, startingExternalIcon)
+
+ // WHEN the external icon is updated
+ val newExternalIcon =
+ StatusBarIcon(
+ "external.package",
+ UserHandle.ALL,
+ /* iconId= */ 21,
+ /* iconLevel= */ 0,
+ /* number= */ 0,
+ "newExternalDescription",
+ )
+ commandQueueCallbacks.setIcon(slotName, newExternalIcon)
// THEN only the external slot gets the updates
val externalSlot = iconList.slots[0]
@@ -289,8 +375,16 @@
}
@Test
- fun externalSlot_alreadyEndsWithSuffix_suffixNotAddedTwice() {
- underTest.setIcon("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
+ fun externalSlot_fromTile_alreadyEndsWithSuffix_suffixNotAddedTwice() {
+ underTest.setIconFromTile("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
+
+ assertThat(iconList.slots).hasSize(1)
+ assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
+ }
+
+ @Test
+ fun externalSlot_fromCommandQueue_alreadyEndsWithSuffix_suffixNotAddedTwice() {
+ commandQueueCallbacks.setIcon("myslot$EXTERNAL_SLOT_SUFFIX", createExternalIcon())
assertThat(iconList.slots).hasSize(1)
assertThat(iconList.slots[0].name).isEqualTo("myslot$EXTERNAL_SLOT_SUFFIX")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index d9546877..14aee4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -154,7 +154,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 64545b1..be0c83f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -294,6 +294,13 @@
}
@Test
+ public void userChip_defaultVisibilityIsGone() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+ assertEquals(View.GONE, getUserChipView().getVisibility());
+ }
+
+ @Test
public void disable_noOngoingCall_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -333,6 +340,19 @@
}
@Test
+ public void disable_hasOngoingCallButAlsoHun_chipHidden() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.GONE,
+ mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ }
+
+ @Test
public void disable_ongoingCallEnded_chipHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -558,6 +578,10 @@
return (CollapsedStatusBarFragment) mFragment;
}
+ private View getUserChipView() {
+ return mFragment.getView().findViewById(R.id.user_switcher_container);
+ }
+
private View getClockView() {
return mFragment.getView().findViewById(R.id.clock);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 542b688..934e1c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -25,7 +25,6 @@
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.DataActivityListener
import android.telephony.TelephonyCallback.ServiceStateListener
-import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
@@ -68,6 +67,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
@@ -392,13 +392,17 @@
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
- val type = NETWORK_TYPE_UNKNOWN
- val expected = UnknownNetworkType
- val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
+ val ti =
+ telephonyDisplayInfo(
+ networkType = NETWORK_TYPE_UNKNOWN,
+ overrideNetworkType = NETWORK_TYPE_UNKNOWN,
+ )
+
callback.onDisplayInfoChanged(ti)
+ val expected = UnknownNetworkType
assertThat(latest).isEqualTo(expected)
- assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(type))
+ assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(NETWORK_TYPE_UNKNOWN))
job.cancel()
}
@@ -412,14 +416,10 @@
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val overrideType = OVERRIDE_NETWORK_TYPE_NONE
val type = NETWORK_TYPE_LTE
- val expected = DefaultNetworkType(mobileMappings.toIconKey(type))
- val ti =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.overrideNetworkType).thenReturn(overrideType)
- whenever(it.networkType).thenReturn(type)
- }
+ val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = overrideType)
callback.onDisplayInfoChanged(ti)
+ val expected = DefaultNetworkType(mobileMappings.toIconKey(type))
assertThat(latest).isEqualTo(expected)
job.cancel()
@@ -433,14 +433,10 @@
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val type = OVERRIDE_NETWORK_TYPE_LTE_CA
- val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
- val ti =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.networkType).thenReturn(type)
- whenever(it.overrideNetworkType).thenReturn(type)
- }
+ val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = type)
callback.onDisplayInfoChanged(ti)
+ val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
assertThat(latest).isEqualTo(expected)
job.cancel()
@@ -455,14 +451,10 @@
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val unknown = NETWORK_TYPE_UNKNOWN
val type = OVERRIDE_NETWORK_TYPE_LTE_CA
- val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
- val ti =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.networkType).thenReturn(unknown)
- whenever(it.overrideNetworkType).thenReturn(type)
- }
+ val ti = telephonyDisplayInfo(unknown, type)
callback.onDisplayInfoChanged(ti)
+ val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
assertThat(latest).isEqualTo(expected)
job.cancel()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index bbf04ed2..9da9ff7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -64,10 +64,14 @@
*
* Kind of like an interaction test case build just for [TelephonyCallback]
*
- * The list of telephony callbacks we use is: [TelephonyCallback.CarrierNetworkListener]
- * [TelephonyCallback.DataActivityListener] [TelephonyCallback.DataConnectionStateListener]
- * [TelephonyCallback.DataEnabledListener] [TelephonyCallback.DisplayInfoListener]
- * [TelephonyCallback.ServiceStateListener] [TelephonyCallback.SignalStrengthsListener]
+ * The list of telephony callbacks we use is:
+ * - [TelephonyCallback.CarrierNetworkListener]
+ * - [TelephonyCallback.DataActivityListener]
+ * - [TelephonyCallback.DataConnectionStateListener]
+ * - [TelephonyCallback.DataEnabledListener]
+ * - [TelephonyCallback.DisplayInfoListener]
+ * - [TelephonyCallback.ServiceStateListener]
+ * - [TelephonyCallback.SignalStrengthsListener]
*
* Because each of these callbacks comes in on the same callbackFlow, collecting on a field backed
* by only a single callback can immediately create backpressure on the other fields related to a
@@ -201,7 +205,6 @@
200 /* unused */
)
- // Send a bunch of events that we don't care about, to overrun the replay buffer
flipActivity(100, activityCallback)
val connectionJob = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
@@ -225,7 +228,6 @@
enabledCallback.onDataEnabledChanged(true, 1 /* unused */)
- // Send a bunch of events that we don't care about, to overrun the replay buffer
flipActivity(100, activityCallback)
val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
@@ -252,7 +254,6 @@
val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
displayInfoCallback.onDisplayInfoChanged(ti)
- // Send a bunch of events that we don't care about, to overrun the replay buffer
flipActivity(100, activityCallback)
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
index d07b96f..cf815c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
@@ -19,6 +19,7 @@
import android.telephony.CellSignalStrengthCdma
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
+import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -48,6 +49,12 @@
return signalStrength
}
+ fun telephonyDisplayInfo(networkType: Int, overrideNetworkType: Int) =
+ mock<TelephonyDisplayInfo>().also {
+ whenever(it.networkType).thenReturn(networkType)
+ whenever(it.overrideNetworkType).thenReturn(overrideNetworkType)
+ }
+
inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T {
val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>()
assertThat(cbs.size).isEqualTo(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 586bdc6..6e24941 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -685,7 +685,7 @@
allowSwipeToDismiss: Boolean = false,
): ChipbarInfo {
return ChipbarInfo(
- TintedIcon(startIcon, tintAttr = null),
+ TintedIcon(startIcon, tint = null),
text,
endItem,
vibrationEffect,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index ee4e00b..cc3b4ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -284,6 +284,8 @@
private ShadeWindowLogger mShadeWindowLogger;
@Mock
private NotifPipelineFlags mNotifPipelineFlags;
+ @Mock
+ private Icon mAppBubbleIcon;
private TestableBubblePositioner mPositioner;
@@ -303,7 +305,7 @@
// For the purposes of this test, just run everything synchronously
ShellExecutor syncExecutor = new SyncExecutor();
- mUser0 = createUserHande(/* userId= */ 0);
+ mUser0 = createUserHandle(/* userId= */ 0);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
when(mNotificationShadeWindowView.getViewTreeObserver())
@@ -1250,7 +1252,7 @@
@Test
public void testShowManageMenuChangesSysuiState_appBubble() {
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
assertTrue(mBubbleController.hasBubbles());
// Expand the stack
@@ -1671,7 +1673,7 @@
assertThat(mBubbleController.isStackExpanded()).isFalse();
assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
verify(mBubbleController).inflateAndAdd(any(Bubble.class), /* suppressFlyout= */ eq(true),
/* showInShade= */ eq(false));
@@ -1681,13 +1683,13 @@
@Test
public void testShowOrHideAppBubble_expandIfCollapsed() {
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.collapseStack();
assertThat(mBubbleController.isStackExpanded()).isFalse();
// Calling this while collapsed will expand the app bubble
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
assertThat(mBubbleController.isStackExpanded()).isTrue();
@@ -1696,12 +1698,12 @@
@Test
public void testShowOrHideAppBubble_collapseIfSelected() {
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
assertThat(mBubbleController.isStackExpanded()).isTrue();
// Calling this while the app bubble is expanded should collapse the stack
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
assertThat(mBubbleController.isStackExpanded()).isFalse();
@@ -1711,15 +1713,15 @@
@Test
public void testShowOrHideAppBubbleWithNonPrimaryUser_bubbleCollapsedWithExpectedUser() {
- UserHandle user10 = createUserHande(/* userId = */ 10);
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10);
+ UserHandle user10 = createUserHandle(/* userId = */ 10);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
assertThat(mBubbleController.isStackExpanded()).isTrue();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(user10);
// Calling this while the app bubble is expanded should collapse the stack
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
assertThat(mBubbleController.isStackExpanded()).isFalse();
@@ -1729,13 +1731,13 @@
@Test
public void testShowOrHideAppBubble_selectIfNotSelected() {
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(mBubbleEntry.getKey());
assertThat(mBubbleController.isStackExpanded()).isTrue();
- mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
assertThat(mBubbleController.isStackExpanded()).isTrue();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
@@ -1870,7 +1872,7 @@
mBubbleController.onUserChanged(userId);
}
- private UserHandle createUserHande(int userId) {
+ private UserHandle createUserHandle(int userId) {
UserHandle user = mock(UserHandle.class);
when(user.getIdentifier()).thenReturn(userId);
return user;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 926c6c5..c664c99 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -47,7 +47,12 @@
}
@Override
- public void setExternalIcon(String slot) {
+ public void setIconFromTile(String slot, StatusBarIcon icon) {
+
+ }
+
+ @Override
+ public void removeIconForTile(String slot) {
}
@@ -57,11 +62,6 @@
}
@Override
- public void setIcon(String slot, StatusBarIcon icon) {
-
- }
-
- @Override
public void setWifiIcon(String slot, WifiIconState state) {
}
@@ -98,10 +98,6 @@
}
@Override
- public void removeAllIconsForExternalSlot(String slot) {
- }
-
- @Override
public void setIconAccessibilityLiveRegion(String slot, int mode) {
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index ee806a6..1298f63 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -2076,6 +2076,7 @@
ret.outputId.id = output.getId();
ret.physicalCameraId = output.getPhysicalCameraId();
ret.surfaceGroupId = output.getSurfaceGroupId();
+ ret.isMultiResolutionOutput = false;
if (output instanceof SurfaceOutputConfigImpl) {
SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
ret.type = CameraOutputConfig.TYPE_SURFACE;
@@ -2095,6 +2096,7 @@
ret.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
ret.imageFormat = multiResReaderConfig.getImageFormat();
ret.capacity = multiResReaderConfig.getMaxImages();
+ ret.isMultiResolutionOutput = true;
} else {
throw new IllegalStateException("Unknown output config type!");
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 01386da..af5b196 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -383,7 +383,7 @@
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service == null) {
// If we cannot get the service from the services cache, it will call
- // updateRemoteAugmentedAutofillService() finally. Skip call this update again.
+ // updateRemoteFieldClassificationService() finally. Skip call this update again.
getServiceForUserLocked(userId);
} else {
service.updateRemoteFieldClassificationService();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index bea049c..d5dcdaf 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1731,7 +1731,7 @@
}
/**
- * Called when the {@link AutofillManagerService#mAugmentedAutofillResolver}
+ * Called when the {@link AutofillManagerService#mFieldClassificationResolver}
* changed (among other places).
*/
void updateRemoteFieldClassificationService() {
@@ -1742,7 +1742,6 @@
+ "destroying old remote service");
}
mRemoteFieldClassificationService.unbind();
-
mRemoteFieldClassificationService = null;
mRemoteFieldClassificationServiceInfo = null;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index b4e636e..62a2970 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -327,13 +327,11 @@
private int setTemporaryDetectionService(PrintWriter pw) {
final int userId = getNextIntArgRequired();
final String serviceName = getNextArg();
- final int duration = getNextIntArgRequired();
-
if (serviceName == null) {
mService.resetTemporaryDetectionService(userId);
return 0;
}
-
+ final int duration = getNextIntArgRequired();
if (duration <= 0) {
mService.resetTemporaryDetectionService(userId);
return 0;
diff --git a/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java b/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
new file mode 100644
index 0000000..3b30af6
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/FillRequestEventLogger.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_EXPLICITLY_REQUESTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_NORMAL_TRIGGER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_PRE_TRIGGER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_RETRIGGER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.IntDef;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Optional;
+
+/**
+ * Helper class to log Autofill FillRequest filing stats.
+ */
+public final class FillRequestEventLogger {
+ private static final String TAG = "FillRequestEventLogger";
+
+ /**
+ * Reasons why presentation was not shown. These are wrappers around
+ * {@link com.android.os.AtomsProto.AutofillFillRequestReported.RequestTriggerReason}.
+ */
+ @IntDef(prefix = {"TRIGGER_REASON"}, value = {
+ TRIGGER_REASON_UNKNOWN,
+ TRIGGER_REASON_EXPLICITLY_REQUESTED,
+ TRIGGER_REASON_RETRIGGER,
+ TRIGGER_REASON_PRE_TRIGGER,
+ TRIGGER_REASON_NORMAL_TRIGGER,
+ TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TriggerReason {
+ }
+
+ public static final int TRIGGER_REASON_UNKNOWN =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
+ public static final int TRIGGER_REASON_EXPLICITLY_REQUESTED =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_EXPLICITLY_REQUESTED;
+ public static final int TRIGGER_REASON_RETRIGGER =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_RETRIGGER;
+ public static final int TRIGGER_REASON_PRE_TRIGGER =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_PRE_TRIGGER;
+ public static final int TRIGGER_REASON_NORMAL_TRIGGER =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_NORMAL_TRIGGER;
+ public static final int TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
+
+ private final int mSessionId;
+ private Optional<FillRequestEventInternal> mEventInternal;
+
+ private FillRequestEventLogger(int sessionId) {
+ mSessionId = sessionId;
+ mEventInternal = Optional.empty();
+ }
+
+ /**
+ * A factory constructor to create FillRequestEventLogger.
+ */
+ public static FillRequestEventLogger forSessionId(int sessionId) {
+ return new FillRequestEventLogger(sessionId);
+ }
+ /**
+ * Reset mEventInternal before logging for a new request. It shall be called for each
+ * FillRequest.
+ */
+ public void startLogForNewRequest() {
+ if (!mEventInternal.isEmpty()) {
+ Slog.w(TAG, "FillRequestEventLogger is not empty before starting for a new " +
+ "request");
+ }
+ mEventInternal = Optional.of(new FillRequestEventInternal());
+ }
+
+ /**
+ * Set request_id as long as mEventInternal presents.
+ */
+ public void maybeSetRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> event.mRequestId = requestId);
+ }
+
+ /**
+ * Set service_uid as long as mEventInternal presents.
+ */
+ public void maybeSetAutofillServiceUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAutofillServiceUid = uid;
+ });
+ }
+
+ /**
+ * Set inline_suggestion_host_uid as long as mEventInternal presents.
+ */
+ public void maybeSetInlineSuggestionHostUid(Context context, int userId) {
+ mEventInternal.ifPresent(event -> {
+ String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, userId);
+ if (TextUtils.isEmpty(imeString)) {
+ Slog.w(TAG, "No default IME found");
+ return;
+ }
+ ComponentName imeComponent = ComponentName.unflattenFromString(imeString);
+ if (imeComponent == null) {
+ Slog.w(TAG, "No default IME found");
+ return;
+ }
+ int imeUid;
+ String packageName = imeComponent.getPackageName();
+ try {
+ imeUid = context.getPackageManager().getApplicationInfoAsUser(packageName,
+ PackageManager.ApplicationInfoFlags.of(0), userId).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Couldn't find packageName: " + packageName);
+ return;
+ }
+ event.mInlineSuggestionHostUid = imeUid;
+ });
+ }
+
+
+ /**
+ * Set flags as long as mEventInternal presents.
+ */
+ public void maybeSetFlags(int flags) {
+ mEventInternal.ifPresent(event -> {
+ event.mFlags = flags;
+ });
+ }
+
+ /**
+ * Set request_trigger_reason as long as mEventInternal presents.
+ */
+ public void maybeSetRequestTriggerReason(@TriggerReason int reason) {
+ mEventInternal.ifPresent(event -> {
+ event.mRequestTriggerReason = reason;
+ });
+ }
+
+ /**
+ * Set is_augmented as long as mEventInternal presents.
+ */
+ public void maybeSetIsAugmented(boolean val) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsAugmented = val;
+ });
+ }
+
+ /**
+ * Set is_client_suggestion as long as mEventInternal presents.
+ */
+ public void maybeSetIsClientSuggestionFallback(boolean val) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsClientSuggestionFallback = val;
+ });
+ }
+
+ /**
+ * Set is_fill_dialog_eligible as long as mEventInternal presents.
+ */
+ public void maybeSetIsFillDialogEligible(boolean val) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsFillDialogEligible = val;
+ });
+ }
+
+ /**
+ * Set latency_fill_request_sent_millis as long as mEventInternal presents.
+ */
+ public void maybeSetLatencyFillRequestSentMillis(int timestamp) {
+ mEventInternal.ifPresent(event -> {
+ event.mLatencyFillRequestSentMillis = timestamp;
+ });
+ }
+
+ /**
+ * Set app_package_uid as long as mEventInternal presents.
+ */
+ public void maybeSetAppPackageUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAppPackageUid = uid;
+ });
+ }
+
+ /**
+ * Log an AUTOFILL_FILL_REQUEST_REPORTED event.
+ */
+ public void logAndEndEvent() {
+ if (!mEventInternal.isPresent()) {
+ Slog.w(TAG, "Shouldn't be logging AutofillFillRequestReported again for same "
+ + "event");
+ return;
+ }
+ FillRequestEventInternal event = mEventInternal.get();
+ if (sVerbose) {
+ Slog.v(TAG, "Log AutofillFillRequestReported:"
+ + " requestId=" + event.mRequestId
+ + " sessionId=" + mSessionId
+ + " mAutofillServiceUid=" + event.mAutofillServiceUid
+ + " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
+ + " mIsAugmented=" + event.mIsAugmented
+ + " mIsClientSuggestionFallback=" + event.mIsClientSuggestionFallback
+ + " mIsFillDialogEligible=" + event.mIsFillDialogEligible
+ + " mRequestTriggerReason=" + event.mRequestTriggerReason
+ + " mFlags=" + event.mFlags
+ + " mLatencyFillRequestSentMillis=" + event.mLatencyFillRequestSentMillis
+ + " mAppPackageUid=" + event.mAppPackageUid);
+ }
+ FrameworkStatsLog.write(
+ AUTOFILL_FILL_REQUEST_REPORTED,
+ event.mRequestId,
+ mSessionId,
+ event.mAutofillServiceUid,
+ event.mInlineSuggestionHostUid,
+ event.mIsAugmented,
+ event.mIsClientSuggestionFallback,
+ event.mIsFillDialogEligible,
+ event.mRequestTriggerReason,
+ event.mFlags,
+ event.mLatencyFillRequestSentMillis,
+ event.mAppPackageUid);
+ mEventInternal = Optional.empty();
+ }
+
+ private static final class FillRequestEventInternal {
+ int mRequestId;
+ int mAppPackageUid = -1;
+ int mAutofillServiceUid = -1;
+ int mInlineSuggestionHostUid = -1;
+ boolean mIsAugmented = false;
+ boolean mIsClientSuggestionFallback = false;
+ boolean mIsFillDialogEligible = false;
+ int mRequestTriggerReason =
+ AUTOFILL_FILL_REQUEST_REPORTED__REQUEST_TRIGGER_REASON__TRIGGER_REASON_UNKNOWN;
+ int mFlags = -1;
+ int mLatencyFillRequestSentMillis = -1;
+
+ FillRequestEventInternal() {
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 590f472..7d1de40 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -38,6 +38,7 @@
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
import static com.android.server.autofill.Helper.sVerbose;
@@ -71,6 +72,7 @@
@IntDef(prefix = {"NOT_SHOWN_REASON"}, value = {
NOT_SHOWN_REASON_ANY_SHOWN,
NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
+ NOT_SHOWN_REASON_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE,
NOT_SHOWN_REASON_VIEW_CHANGED,
NOT_SHOWN_REASON_ACTIVITY_FINISHED,
NOT_SHOWN_REASON_REQUEST_TIMEOUT,
@@ -86,6 +88,8 @@
AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
+ public static final int NOT_SHOWN_REASON_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUSED_BEFORE_FILL_DIALOG_RESPONSE;
public static final int NOT_SHOWN_REASON_VIEW_CHANGED =
AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED =
@@ -142,7 +146,7 @@
}
public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
- AutofillId currentViewId) {
+ AutofillId currentViewId) {
mEventInternal.ifPresent(event -> {
int availableCount = getDatasetCountForAutofillId(datasetList, currentViewId);
event.mAvailableCount = availableCount;
@@ -151,7 +155,7 @@
}
public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
- AutofillId currentViewId) {
+ AutofillId currentViewId) {
mEventInternal.ifPresent(event -> {
int countShown = getDatasetCountForAutofillId(datasetList, currentViewId);
event.mCountShown = countShown;
@@ -162,7 +166,7 @@
}
private static int getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
- AutofillId currentViewId) {
+ AutofillId currentViewId) {
int availableCount = 0;
if (datasetList != null) {
for (int i = 0; i < datasetList.size(); i++) {
@@ -225,10 +229,34 @@
});
}
+ public void maybeSetSelectedDatasetId(int selectedDatasetId) {
+ mEventInternal.ifPresent(event -> {
+ event.mSelectedDatasetId = selectedDatasetId;
+ });
+ }
+
+ public void maybeSetDialogDismissed(boolean dialogDismissed) {
+ mEventInternal.ifPresent(event -> {
+ event.mDialogDismissed = dialogDismissed;
+ });
+ }
+
+ public void maybeSetNegativeCtaButtonClicked(boolean negativeCtaButtonClicked) {
+ mEventInternal.ifPresent(event -> {
+ event.mNegativeCtaButtonClicked = negativeCtaButtonClicked;
+ });
+ }
+
+ public void maybeSetPositiveCtaButtonClicked(boolean positiveCtaButtonClicked) {
+ mEventInternal.ifPresent(event -> {
+ event.mPositiveCtaButtonClicked = positiveCtaButtonClicked;
+ });
+ }
+
public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
mEventInternal.ifPresent(event -> {
event.mDisplayPresentationType =
- AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, userId);
if (TextUtils.isEmpty(imeString)) {
@@ -290,7 +318,11 @@
+ " mFillRequestSentTimestampMs=" + event.mFillRequestSentTimestampMs
+ " mFillResponseReceivedTimestampMs=" + event.mFillResponseReceivedTimestampMs
+ " mSuggestionSentTimestampMs=" + event.mSuggestionSentTimestampMs
- + " mSuggestionPresentedTimestampMs=" + event.mSuggestionPresentedTimestampMs);
+ + " mSuggestionPresentedTimestampMs=" + event.mSuggestionPresentedTimestampMs
+ + " mSelectedDatasetId=" + event.mSelectedDatasetId
+ + " mDialogDismissed=" + event.mDialogDismissed
+ + " mNegativeCtaButtonClicked=" + event.mNegativeCtaButtonClicked
+ + " mPositiveCtaButtonClicked=" + event.mPositiveCtaButtonClicked);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -316,11 +348,10 @@
event.mFillResponseReceivedTimestampMs,
event.mSuggestionSentTimestampMs,
event.mSuggestionPresentedTimestampMs,
- //TODO(b/265051751): add new framework logging.
- /* selected_dataset_id= */ 0,
- /* dialog_dismissed= */ false,
- /* negative_cta_button_clicked= */ false,
- /* positive_cta_button_clicked= */ false);
+ event.mSelectedDatasetId,
+ event.mDialogDismissed,
+ event.mNegativeCtaButtonClicked,
+ event.mPositiveCtaButtonClicked);
mEventInternal = Optional.empty();
}
@@ -341,6 +372,10 @@
int mFillResponseReceivedTimestampMs;
int mSuggestionSentTimestampMs;
int mSuggestionPresentedTimestampMs;
+ int mSelectedDatasetId = -1;
+ boolean mDialogDismissed = false;
+ boolean mNegativeCtaButtonClicked = false;
+ boolean mPositiveCtaButtonClicked = false;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 15a533c..4acdabe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -41,6 +41,9 @@
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
+import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_PRE_TRIGGER;
+import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
import static com.android.server.autofill.Helper.containsCharsInOrder;
import static com.android.server.autofill.Helper.createSanitizers;
import static com.android.server.autofill.Helper.getNumericValue;
@@ -439,6 +442,10 @@
@GuardedBy("mLock")
private PresentationStatsEventLogger mPresentationStatsEventLogger;
+ @NonNull
+ @GuardedBy("mLock")
+ private FillRequestEventLogger mFillRequestEventLogger;
+
/**
* Fill dialog request would likely be sent slightly later.
*/
@@ -605,6 +612,14 @@
mPendingInlineSuggestionsRequest = null;
mWaitForInlineRequest = false;
mPendingFillRequest = null;
+
+ final long fillRequestSentRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetFillRequestSentTimestampMs(
+ (int) (fillRequestSentRelativeTimestamp));
+ mFillRequestEventLogger.maybeSetLatencyFillRequestSentMillis(
+ (int) (fillRequestSentRelativeTimestamp));
+ mFillRequestEventLogger.logAndEndEvent();
}
@Override
@@ -1082,11 +1097,21 @@
private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
int flags) {
final FillResponse existingResponse = viewState.getResponse();
+ mFillRequestEventLogger.startLogForNewRequest();
+ mFillRequestEventLogger.maybeSetAppPackageUid(uid);
+ mFillRequestEventLogger.maybeSetFlags(mFlags);
+ if(mPreviouslyFillDialogPotentiallyStarted) {
+ mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER);
+ } else {
+ mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER);
+ }
if (existingResponse != null) {
setViewStatesLocked(
existingResponse,
ViewState.STATE_INITIAL,
/* clearResponse= */ true);
+ mFillRequestEventLogger.maybeSetRequestTriggerReason(
+ TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE);
}
mSessionFlags.mExpiredResponse = false;
mSessionState = STATE_ACTIVE;
@@ -1097,6 +1122,10 @@
+ ", flags=" + flags + ")");
}
mSessionFlags.mAugmentedAutofillOnly = true;
+ // Augmented autofill doesn't have request_id.
+ mFillRequestEventLogger.maybeSetRequestId(-1);
+ mFillRequestEventLogger.maybeSetIsAugmented(mSessionFlags.mAugmentedAutofillOnly);
+ mFillRequestEventLogger.logAndEndEvent();
triggerAugmentedAutofillLocked(flags);
return;
}
@@ -1123,6 +1152,12 @@
+ ", flags=" + flags);
}
mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+ mFillRequestEventLogger.maybeSetRequestId(requestId);
+ mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+ if (mSessionFlags.mInlineSupportedByService) {
+ mFillRequestEventLogger.maybeSetInlineSuggestionHostUid(mContext, userId);
+ }
+ mFillRequestEventLogger.maybeSetIsFillDialogEligible(!mSessionFlags.mFillDialogDisabled);
// If the focus changes very quickly before the first request is returned each focus change
// triggers a new partition and we end up with many duplicate partitions. This is
@@ -1189,11 +1224,6 @@
return;
}
- final long fillRequestSentRelativeTimestamp =
- SystemClock.elapsedRealtime() - mLatencyBaseTime;
- mPresentationStatsEventLogger.maybeSetFillRequestSentTimestampMs(
- (int) (fillRequestSentRelativeTimestamp));
-
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
}
@@ -1284,6 +1314,7 @@
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
+ mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
synchronized (mLock) {
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index b673fb6..97af17d 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -44,6 +44,7 @@
import android.util.SparseArray;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.KnownPackages;
import com.android.server.pm.PackageList;
@@ -1077,10 +1078,17 @@
/**
* Read legacy permission definitions for permissions migration to new permission subsystem.
+ * Note that this api is supposed to be used for permissions migration only.
*/
public abstract LegacyPermissionSettings getLegacyPermissions();
/**
+ * Read legacy permission states for permissions migration to new permission subsystem.
+ * Note that this api is supposed to be used for permissions state migration only.
+ */
+ public abstract RuntimePermissionsState getLegacyPermissionsState(@UserIdInt int userId);
+
+ /**
* Returns {@code true} if the caller is the installer of record for the given package.
* Otherwise, {@code false}.
*/
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 742c94a..d6f1348 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -343,10 +343,8 @@
// Check if user is associated with the subscription
if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
Binder.getCallingUserHandle())) {
- if (TelephonyUtils.isUidForeground(mContext, Binder.getCallingUid())) {
- TelephonyUtils.showErrorIfSubscriptionAssociatedWithManagedProfile(mContext,
- subId);
- }
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext,
+ subId, Binder.getCallingUid(), callingPkg);
return;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4a0a228..461103e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -602,7 +602,7 @@
try {
final ServiceRecord.StartItem si = r.pendingStarts.get(0);
startServiceInnerLocked(this, si.intent, r, false, true, si.callingId,
- si.mCallingProcessName, r.startRequested);
+ si.mCallingProcessName, r.startRequested, si.mCallingPackageName);
} catch (TransactionTooLargeException e) {
// Ignore, nobody upstack cares.
}
@@ -969,7 +969,7 @@
startServiceInnerLocked(r, service, callingUid, callingPid,
getCallingProcessNameLocked(callingUid, callingPid, callingPackage),
fgRequired, callerFg,
- backgroundStartPrivileges);
+ backgroundStartPrivileges, callingPackage);
if (res.aliasComponent != null
&& !realResult.getPackageName().startsWith("!")
&& !realResult.getPackageName().startsWith("?")) {
@@ -990,7 +990,7 @@
private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
int callingUid, int callingPid, String callingProcessName, boolean fgRequired,
boolean callerFg,
- BackgroundStartPrivileges backgroundStartPrivileges)
+ BackgroundStartPrivileges backgroundStartPrivileges, String callingPackage)
throws TransactionTooLargeException {
NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
service, callingUid, r.packageName, r.userId);
@@ -1003,7 +1003,7 @@
r.delayedStop = false;
r.fgRequired = fgRequired;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
- service, neededGrants, callingUid, callingProcessName));
+ service, neededGrants, callingUid, callingProcessName, callingPackage));
if (fgRequired) {
// We are now effectively running a foreground service.
@@ -1088,7 +1088,7 @@
r.allowBgActivityStartsOnServiceStart(backgroundStartPrivileges);
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
- callingUid, callingProcessName, wasStartRequested);
+ callingUid, callingProcessName, wasStartRequested, callingPackage);
return cmp;
}
@@ -1241,7 +1241,7 @@
try {
startServiceInnerLocked(s, serviceIntent, callingUid, callingPid,
callingProcessName, fgRequired, callerFg,
- backgroundStartPrivileges);
+ backgroundStartPrivileges, callingPackage);
} catch (TransactionTooLargeException e) {
/* ignore - local call */
}
@@ -1287,7 +1287,7 @@
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting, int callingUid, String callingProcessName,
- boolean wasStartRequested) throws TransactionTooLargeException {
+ boolean wasStartRequested, String callingPackage) throws TransactionTooLargeException {
synchronized (mAm.mProcessStats.mLock) {
final ServiceState stracker = r.getTracker();
if (stracker != null) {
@@ -1328,7 +1328,9 @@
: SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM),
getShortProcessNameForStats(callingUid, callingProcessName),
getShortServiceNameForStats(r),
- packageState);
+ packageState,
+ packageName,
+ callingPackage);
if (r.startRequested && addToStarting) {
boolean first = smap.mStartingBackground.size() == 0;
@@ -3661,7 +3663,9 @@
: SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM),
getShortProcessNameForStats(callingUid, callerApp.processName),
getShortServiceNameForStats(s),
- packageState);
+ packageState,
+ s.packageName,
+ callerApp.info.packageName);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
+ ": received=" + b.intent.received
@@ -5253,7 +5257,7 @@
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
- null, null, 0, null));
+ null, null, 0, null, null));
}
sendServiceArgsLocked(r, execInFg, true);
@@ -6247,7 +6251,7 @@
stopServiceLocked(sr, true);
} else {
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
- sr.getLastStartId(), baseIntent, null, 0, null));
+ sr.getLastStartId(), baseIntent, null, 0, null, null));
if (sr.app != null && sr.app.getThread() != null) {
// We always run in the foreground, since this is called as
// part of the "remove task" UI operation.
@@ -7318,7 +7322,7 @@
if (!r.mAllowWhileInUsePermissionInFgs
|| (r.mAllowStartForeground == REASON_DENIED)) {
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
- callingPackage, callingPid, callingUid, r, backgroundStartPrivileges,
+ callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges,
isBindService);
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
@@ -7345,7 +7349,7 @@
return true;
}
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
- callingPackage, callingPid, callingUid, null /* serviceRecord */,
+ callingPackage, callingPid, callingUid, null /* targetProcess */,
BackgroundStartPrivileges.NONE, false);
@ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked(
allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */,
@@ -7362,13 +7366,14 @@
/**
* Should allow while-in-use permissions in FGS or not.
* A typical BG started FGS is not allowed to have while-in-use permissions.
+ *
* @param callingPackage caller app's package name.
- * @param callingUid caller app's uid.
- * @param targetService the service to start.
+ * @param callingUid caller app's uid.
+ * @param targetProcess the process of the service to start.
* @return {@link ReasonCode}
*/
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
- int callingPid, int callingUid, @Nullable ServiceRecord targetService,
+ int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
int ret = REASON_DENIED;
@@ -7436,8 +7441,8 @@
}
if (ret == REASON_DENIED) {
- if (targetService != null && targetService.app != null) {
- ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
+ if (targetProcess != null) {
+ ActiveInstrumentation instr = targetProcess.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
}
@@ -7522,7 +7527,7 @@
final @ReasonCode int allowWhileInUse2 =
shouldAllowFgsWhileInUsePermissionLocked(
clientPackageName,
- clientPid, clientUid, null /* serviceRecord */,
+ clientPid, clientUid, null /* targetProcess */,
BackgroundStartPrivileges.NONE, false);
final @ReasonCode int allowStartFgs =
shouldAllowFgsStartForegroundNoBindingCheckLocked(
@@ -7942,11 +7947,18 @@
boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
String callingPackage) {
return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
- /* targetService */ null,
+ /* targetProcess */ null,
BackgroundStartPrivileges.NONE, false)
!= REASON_DENIED;
}
+ boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
+ String callingPackage, @Nullable ProcessRecord targetProcess,
+ @NonNull BackgroundStartPrivileges backgroundStartPrivileges) {
+ return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
+ targetProcess, backgroundStartPrivileges, false) != REASON_DENIED;
+ }
+
/**
* Checks if a given packageName belongs to a given uid.
* @param packageName the package of the caller
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4fa28a1..82c4796a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -118,6 +118,8 @@
static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit";
static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration";
static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration";
+ static final String KEY_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION =
+ "vis_to_invis_uij_schedule_grace_duration";
static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate";
static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate";
@@ -191,6 +193,8 @@
private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12;
private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 20 * 1000;
private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000;
+ private static final long DEFAULT_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION =
+ DEFAULT_FG_TO_BG_FGS_GRACE_DURATION;
private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 %
private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25%
@@ -680,6 +684,15 @@
volatile long mFgToBgFgsGraceDuration = DEFAULT_FG_TO_BG_FGS_GRACE_DURATION;
/**
+ * The grace period in milliseconds to allow a process to schedule a
+ * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}
+ * after switching from visible to a non-visible state.
+ * Currently it's only applicable to its activities.
+ */
+ volatile long mVisibleToInvisibleUijScheduleGraceDurationMs =
+ DEFAULT_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION;
+
+ /**
* When service started from background, before the timeout it can be promoted to FGS by calling
* Service.startForeground().
*/
@@ -1123,6 +1136,9 @@
case KEY_FG_TO_BG_FGS_GRACE_DURATION:
updateFgToBgFgsGraceDuration();
break;
+ case KEY_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION:
+ updateFgToBgFgsGraceDuration();
+ break;
case KEY_FGS_START_FOREGROUND_TIMEOUT:
updateFgsStartForegroundTimeout();
break;
@@ -1598,6 +1614,13 @@
DEFAULT_FG_TO_BG_FGS_GRACE_DURATION);
}
+ private void updateVisibleToInvisibleUijScheduleGraceDuration() {
+ mVisibleToInvisibleUijScheduleGraceDurationMs = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION,
+ DEFAULT_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION);
+ }
+
private void updateFgsStartForegroundTimeout() {
mFgsStartForegroundTimeoutMs = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4038606..9eb482e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -23,6 +23,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.MANAGE_USERS;
+import static android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
@@ -32,6 +33,7 @@
import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -60,9 +62,22 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
+import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
+import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
+import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
+import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP;
+import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
+import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
+import static android.os.PowerExemptionManager.REASON_PROC_STATE_TOP;
+import static android.os.PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
+import static android.os.PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
+import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
+import static android.os.PowerExemptionManager.REASON_UID_VISIBLE;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
+import static android.os.PowerExemptionManager.getReasonCodeFromProcState;
import static android.os.Process.BLUETOOTH_UID;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
@@ -6096,6 +6111,143 @@
}
/**
+ * Returns true if the reasonCode is included in the base set of reasons an app may be
+ * allowed to schedule a
+ * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
+ * This is a shortcut and <b>DOES NOT</b> include all reasons.
+ * Use {@link #canScheduleUserInitiatedJobs(int, int, String)} to cover all cases.
+ */
+ private boolean doesReasonCodeAllowSchedulingUserInitiatedJobs(int reasonCode) {
+ switch (reasonCode) {
+ case REASON_PROC_STATE_PERSISTENT:
+ case REASON_PROC_STATE_PERSISTENT_UI:
+ case REASON_PROC_STATE_TOP:
+ case REASON_PROC_STATE_BTOP:
+ case REASON_UID_VISIBLE:
+ case REASON_SYSTEM_UID:
+ case REASON_START_ACTIVITY_FLAG:
+ case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD:
+ case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
+ case REASON_COMPANION_DEVICE_MANAGER:
+ case REASON_BACKGROUND_ACTIVITY_PERMISSION:
+ case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the ProcessRecord has some conditions that allow the app to schedule a
+ * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
+ * This is a shortcut and <b>DOES NOT</b> include all reasons.
+ * Use {@link #canScheduleUserInitiatedJobs(int, int, String)} to cover all cases.
+ */
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ private boolean isProcessInStateToScheduleUserInitiatedJobsLocked(
+ @Nullable ProcessRecord pr, long nowElapsed) {
+ if (pr == null) {
+ return false;
+ }
+
+ final BackgroundStartPrivileges backgroundStartPrivileges =
+ pr.getBackgroundStartPrivileges();
+ // Is the allow activity background start flag on?
+ if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
+ // REASON_START_ACTIVITY_FLAG;
+ return true;
+ }
+
+ final ProcessStateRecord state = pr.mState;
+ final int procstate = state.getCurProcState();
+ if (procstate <= PROCESS_STATE_BOUND_TOP) {
+ if (doesReasonCodeAllowSchedulingUserInitiatedJobs(
+ getReasonCodeFromProcState(procstate))) {
+ return true;
+ }
+ }
+
+ final long lastInvisibleTime = state.getLastInvisibleTime();
+ if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE) {
+ final long timeSinceVisibleMs = nowElapsed - lastInvisibleTime;
+ if (timeSinceVisibleMs < mConstants.mVisibleToInvisibleUijScheduleGraceDurationMs) {
+ // REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the app in question is in a state where we allow scheduling a
+ * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
+ */
+ // TODO(262260570): log allow reason to an atom
+ private boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
+ synchronized (this) {
+ final ProcessRecord processRecord;
+ synchronized (mPidsSelfLocked) {
+ processRecord = mPidsSelfLocked.get(pid);
+ }
+
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final BackgroundStartPrivileges backgroundStartPrivileges;
+ if (processRecord != null) {
+ if (isProcessInStateToScheduleUserInitiatedJobsLocked(processRecord, nowElapsed)) {
+ return true;
+ }
+ backgroundStartPrivileges = processRecord.getBackgroundStartPrivileges();
+ } else {
+ backgroundStartPrivileges = getBackgroundStartPrivileges(uid);
+ }
+ // Is the allow activity background start flag on?
+ if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
+ // REASON_START_ACTIVITY_FLAG;
+ return true;
+ }
+
+ // We allow scheduling a user-initiated job when the app is in the TOP or a
+ // Background Activity Launch approved state. These are cases that indicate the user
+ // has interacted with the app and therefore it is reasonable to believe the app may
+ // attempt to schedule a user-initiated job in response to the user interaction.
+ // As of Android UDC, the conditions required to grant a while-in-use permission
+ // covers the majority of those cases, and so we piggyback on that logic as the base.
+ // Missing cases are added after.
+ if (mServices.canAllowWhileInUsePermissionInFgsLocked(
+ pid, uid, pkgName, processRecord, backgroundStartPrivileges)) {
+ return true;
+ }
+
+ final UidRecord uidRecord = mProcessList.getUidRecordLOSP(uid);
+ if (uidRecord != null) {
+ for (int i = uidRecord.getNumOfProcs() - 1; i >= 0; --i) {
+ ProcessRecord pr = uidRecord.getProcessRecordByIndex(i);
+ if (isProcessInStateToScheduleUserInitiatedJobsLocked(pr, nowElapsed)) {
+ return true;
+ }
+ }
+ }
+
+ if (mAtmInternal.hasSystemAlertWindowPermission(uid, pid, pkgName)) {
+ // REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ return true;
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ final boolean isCompanionApp = mInternal.isAssociatedCompanionApp(userId, uid);
+ if (isCompanionApp) {
+ if (checkPermission(REQUEST_COMPANION_RUN_IN_BACKGROUND, pid, uid)
+ == PERMISSION_GRANTED) {
+ // REASON_COMPANION_DEVICE_MANAGER;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
* @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
* the allowlist
*/
@@ -13488,7 +13640,7 @@
if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
/*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
throw new SecurityException("SDK sandbox not allowed to register receiver"
- + " with the given IntentFilter");
+ + " with the given IntentFilter: " + filter.toString());
}
}
@@ -17711,6 +17863,11 @@
return ActivityManagerService.this.getBackgroundStartPrivileges(uid);
}
+ @Override
+ public boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
+ return ActivityManagerService.this.canScheduleUserInitiatedJobs(uid, pid, pkgName);
+ }
+
public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
ActivityManagerService.this.reportGlobalUsageEvent(keyguardShowing
? UsageEvents.Event.KEYGUARD_SHOWN
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 16219cd..8325858 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -22,6 +22,7 @@
import android.content.pm.ApplicationInfo;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -29,8 +30,12 @@
import com.android.internal.os.TimeoutRecord;
import com.android.server.wm.WindowProcessController;
+import java.io.File;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
@@ -59,13 +64,19 @@
/**
* The keep alive time for the threads in the helper threadpool executor
*/
- private static final int AUX_THREAD_KEEP_ALIVE_SECOND = 10;
+ private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10;
private static final ThreadFactory sDefaultThreadFactory = r ->
new Thread(r, "AnrAuxiliaryTaskExecutor");
+ private static final ThreadFactory sMainProcessDumpThreadFactory = r ->
+ new Thread(r, "AnrMainProcessDumpThread");
@GuardedBy("mAnrRecords")
private final ArrayList<AnrRecord> mAnrRecords = new ArrayList<>();
+
+ private final Set<Integer> mTempDumpedPids =
+ Collections.synchronizedSet(new ArraySet<Integer>());
+
private final AtomicBoolean mRunning = new AtomicBoolean(false);
private final ActivityManagerService mService;
@@ -80,17 +91,23 @@
private int mProcessingPid = -1;
private final ExecutorService mAuxiliaryTaskExecutor;
+ private final ExecutorService mEarlyDumpExecutor;
AnrHelper(final ActivityManagerService service) {
this(service, new ThreadPoolExecutor(/* corePoolSize= */ 0, /* maximumPoolSize= */ 1,
- /* keepAliveTime= */ AUX_THREAD_KEEP_ALIVE_SECOND, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(), sDefaultThreadFactory));
+ /* keepAliveTime= */ DEFAULT_THREAD_KEEP_ALIVE_SECOND, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(), sDefaultThreadFactory),
+ new ThreadPoolExecutor(/* corePoolSize= */ 0, /* maximumPoolSize= */ 2,
+ /* keepAliveTime= */ DEFAULT_THREAD_KEEP_ALIVE_SECOND, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(), sMainProcessDumpThreadFactory));
}
@VisibleForTesting
- AnrHelper(ActivityManagerService service, ExecutorService auxExecutor) {
+ AnrHelper(ActivityManagerService service, ExecutorService auxExecutor,
+ ExecutorService earlyDumpExecutor) {
mService = service;
mAuxiliaryTaskExecutor = auxExecutor;
+ mEarlyDumpExecutor = earlyDumpExecutor;
}
void appNotResponding(ProcessRecord anrProcess, TimeoutRecord timeoutRecord) {
@@ -121,6 +138,12 @@
+ timeoutRecord.mReason);
return;
}
+ if (!mTempDumpedPids.add(incomingPid)) {
+ Slog.i(TAG,
+ "Skip ANR being predumped, pid=" + incomingPid + " "
+ + timeoutRecord.mReason);
+ return;
+ }
for (int i = mAnrRecords.size() - 1; i >= 0; i--) {
if (mAnrRecords.get(i).mPid == incomingPid) {
Slog.i(TAG,
@@ -129,10 +152,24 @@
return;
}
}
+ // We dump the main process as soon as we can on a different thread,
+ // this is done as the main process's dump can go stale in a few hundred
+ // milliseconds and the average full ANR dump takes a few seconds.
+ timeoutRecord.mLatencyTracker.earlyDumpRequestSubmittedWithSize(
+ mTempDumpedPids.size());
+ Future<File> firstPidDumpPromise = mEarlyDumpExecutor.submit(() -> {
+ // the class AnrLatencyTracker is not generally thread safe but the values
+ // recorded/touched by the Temporary dump thread(s) are all volatile/atomic.
+ File tracesFile = StackTracesDumpHelper.dumpStackTracesTempFile(incomingPid,
+ timeoutRecord.mLatencyTracker);
+ mTempDumpedPids.remove(incomingPid);
+ return tracesFile;
+ });
+
timeoutRecord.mLatencyTracker.anrRecordPlacingOnQueueWithSize(mAnrRecords.size());
mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
- parentShortComponentName, parentProcess, aboveSystem,
- mAuxiliaryTaskExecutor, timeoutRecord, isContinuousAnr));
+ parentShortComponentName, parentProcess, aboveSystem, timeoutRecord,
+ isContinuousAnr, firstPidDumpPromise));
}
startAnrConsumerIfNeeded();
} finally {
@@ -219,7 +256,7 @@
}
}
- private static class AnrRecord {
+ private class AnrRecord {
final ProcessRecord mApp;
final int mPid;
final String mActivityShortComponentName;
@@ -228,14 +265,14 @@
final ApplicationInfo mAppInfo;
final WindowProcessController mParentProcess;
final boolean mAboveSystem;
- final ExecutorService mAuxiliaryTaskExecutor;
final long mTimestamp = SystemClock.uptimeMillis();
final boolean mIsContinuousAnr;
+ final Future<File> mFirstPidFilePromise;
AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem,
- ExecutorService auxiliaryTaskExecutor, TimeoutRecord timeoutRecord,
- boolean isContinuousAnr) {
+ TimeoutRecord timeoutRecord, boolean isContinuousAnr,
+ Future<File> firstPidFilePromise) {
mApp = anrProcess;
mPid = anrProcess.mPid;
mActivityShortComponentName = activityShortComponentName;
@@ -244,8 +281,8 @@
mAppInfo = aInfo;
mParentProcess = parentProcess;
mAboveSystem = aboveSystem;
- mAuxiliaryTaskExecutor = auxiliaryTaskExecutor;
mIsContinuousAnr = isContinuousAnr;
+ mFirstPidFilePromise = firstPidFilePromise;
}
void appNotResponding(boolean onlyDumpSelf) {
@@ -254,7 +291,7 @@
mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
mParentShortComponentName, mParentProcess, mAboveSystem,
mTimeoutRecord, mAuxiliaryTaskExecutor, onlyDumpSelf,
- mIsContinuousAnr);
+ mIsContinuousAnr, mFirstPidFilePromise);
} finally {
mTimeoutRecord.mLatencyTracker.anrProcessingEnded();
}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 2d779c4..6928bd3 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -106,7 +106,6 @@
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -117,7 +116,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
import android.content.pm.ServiceInfo.ForegroundServiceType;
-import android.database.ContentObserver;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AppBackgroundRestrictionsInfo;
@@ -137,7 +135,6 @@
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -1069,8 +1066,7 @@
}
}
- final class ConstantsObserver extends ContentObserver implements
- OnPropertiesChangedListener {
+ final class ConstantsObserver implements OnPropertiesChangedListener {
/**
* Whether or not to set the app to restricted standby bucket automatically
* when it's background-restricted.
@@ -1181,8 +1177,6 @@
volatile boolean mBgAutoRestrictAbusiveApps;
- volatile boolean mRestrictedBucketEnabled;
-
volatile long mBgAbusiveNotificationMinIntervalMs;
volatile long mBgLongFgsNotificationMinIntervalMs;
@@ -1215,7 +1209,6 @@
volatile boolean mBgPromptAbusiveAppsToBgRestricted;
ConstantsObserver(Handler handler, Context context) {
- super(handler);
mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean(
com.android.internal.R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted);
mDefaultBgPromptAbusiveAppToBgRestricted = context.getResources().getBoolean(
@@ -1261,29 +1254,10 @@
}
}
- @Override
- public void onChange(boolean selfChange) {
- updateSettings();
- }
-
public void start() {
- final ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
- false, this);
- updateSettings();
updateDeviceConfig();
}
- void updateSettings() {
- mRestrictedBucketEnabled = isRestrictedBucketEnabled();
- }
-
- private boolean isRestrictedBucketEnabled() {
- return Global.getInt(mContext.getContentResolver(),
- Global.ENABLE_RESTRICTED_BUCKET,
- Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
- }
-
void updateDeviceConfig() {
updateBgAutoRestrictedBucketChanged();
updateBgAutoRestrictAbusiveApps();
@@ -1763,8 +1737,7 @@
.isAppBackgroundRestricted(uid, packageName)) {
return new Pair<>(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, mEmptyTrackerInfo);
}
- level = mConstantsObserver.mRestrictedBucketEnabled
- && standbyBucket == STANDBY_BUCKET_RESTRICTED
+ level = standbyBucket == STANDBY_BUCKET_RESTRICTED
? RESTRICTION_LEVEL_RESTRICTED_BUCKET
: RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
if (calcTrackers) {
@@ -1811,13 +1784,9 @@
@RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
@RestrictionLevel int prevLevel = level;
BaseAppStateTracker resultTracker = null;
- final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled;
for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
@RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
.getProposedRestrictionLevel(packageName, uid, maxLevel);
- if (!isRestrictedBucketEnabled && l == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
- l = RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
- }
level = Math.max(level, l);
if (level != prevLevel) {
resultTracker = mAppStateTrackers.get(i);
@@ -2193,9 +2162,6 @@
}
if (level >= RESTRICTION_LEVEL_RESTRICTED_BUCKET
&& curLevel < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
- if (!mConstantsObserver.mRestrictedBucketEnabled) {
- return;
- }
// Moving the app standby bucket to restricted in the meanwhile.
if (DEBUG_BG_RESTRICTION_CONTROLLER
&& level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 1f65730..0cdd4e9 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -399,7 +399,8 @@
* Update if this process is in the "cached" state, typically signaling that
* broadcast dispatch should be paused or delayed.
*/
- public void setProcessCached(boolean cached) {
+ @VisibleForTesting
+ void setProcessCached(boolean cached) {
if (mProcessCached != cached) {
mProcessCached = cached;
invalidateRunnableAt();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index c085706..fcddff0 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -601,7 +601,9 @@
r.dispatchTime - r.enqueueTime,
r.receiverTime - r.dispatchTime,
finishTime - r.receiverTime,
- packageState);
+ packageState,
+ r.curApp.info.packageName,
+ r.callerPackage);
}
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
@@ -780,7 +782,8 @@
BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
dispatchDelay, receiveDelay, 0 /* finish_delay */,
- SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+ SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+ app != null ? app.info.packageName : null, callingPackage);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8e1f8f9..8edde71 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1122,7 +1122,7 @@
}
r.terminalCount++;
- notifyFinishReceiver(queue, r, index, receiver);
+ notifyFinishReceiver(queue, app, r, index, receiver);
checkFinished = true;
}
// When entire ordered broadcast finished, deliver final result
@@ -1597,9 +1597,10 @@
* typically for internal bookkeeping.
*/
private void notifyFinishReceiver(@Nullable BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
+ @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+ @NonNull Object receiver) {
if (r.wasDeliveryAttempted(index)) {
- logBroadcastDeliveryEventReported(queue, r, index, receiver);
+ logBroadcastDeliveryEventReported(queue, app, r, index, receiver);
}
final boolean recordFinished = (r.terminalCount == r.receivers.size());
@@ -1609,7 +1610,8 @@
}
private void logBroadcastDeliveryEventReported(@Nullable BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
+ @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+ @NonNull Object receiver) {
// Report statistics for each individual receiver
final int uid = getReceiverUid(receiver);
final int senderUid = (r.callingUid == -1) ? Process.SYSTEM_UID : r.callingUid;
@@ -1635,7 +1637,8 @@
? SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
: SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName,
- receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState);
+ receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState,
+ app != null ? app.info.packageName : null, r.callerPackage);
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 3ab9e71..e8b65b8 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1914,7 +1914,9 @@
uids[0] = uid;
frozenStates[0] = frozen ? UID_FROZEN_STATE_FROZEN : UID_FROZEN_STATE_UNFROZEN;
- Slog.d(TAG_AM, "reportOneUidFrozenStateChanged uid " + uid + " frozen = " + frozen);
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "reportOneUidFrozenStateChanged uid " + uid + " frozen = " + frozen);
+ }
mAm.reportUidFrozenStateChanged(uids, frozenStates);
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index bb5f31a..824509a 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -262,7 +262,8 @@
PROVIDER_ACQUISITION_EVENT_REPORTED,
r.uid, callingUid,
PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
- PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+ cpi.packageName, callingPackage);
return holder;
}
@@ -333,7 +334,8 @@
PROVIDER_ACQUISITION_EVENT_REPORTED,
cpr.proc.uid, callingUid,
PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
- PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+ cpi.packageName, callingPackage);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -509,7 +511,8 @@
PROVIDER_ACQUISITION_EVENT_REPORTED,
proc.uid, callingUid,
PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
- PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL);
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
+ cpi.packageName, callingPackage);
} else {
final int packageState =
((cpr.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0)
@@ -534,7 +537,7 @@
PROVIDER_ACQUISITION_EVENT_REPORTED,
proc.uid, callingUid,
PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
- packageState);
+ packageState, cpi.packageName, callingPackage);
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 0f8871c..7788ea4 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -287,7 +287,7 @@
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, TimeoutRecord timeoutRecord,
ExecutorService auxiliaryTaskExecutor, boolean onlyDumpSelf,
- boolean isContinuousAnr) {
+ boolean isContinuousAnr, Future<File> firstPidFilePromise) {
String annotation = timeoutRecord.mReason;
AnrLatencyTracker latencyTracker = timeoutRecord.mLatencyTracker;
Future<?> updateCpuStatsNowFirstCall = null;
@@ -334,7 +334,6 @@
Counter.logIncrement("stability_anr.value_skipped_anrs");
return;
}
-
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
latencyTracker.waitingOnProcLockStarted();
@@ -368,6 +367,9 @@
firstPids.add(pid);
// Don't dump other PIDs if it's a background ANR or is requested to only dump self.
+ // Note that the primary pid is added here just in case, as it should normally be
+ // dumped on the early dump thread, and would only be dumped on the Anr consumer thread
+ // as a fallback.
isSilentAnr = isSilentAnr();
if (!isSilentAnr && !onlyDumpSelf) {
int parentPid = pid;
@@ -498,7 +500,7 @@
File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePidsFuture, tracesFileException, firstPidEndOffset, annotation,
- criticalEventLog, auxiliaryTaskExecutor, latencyTracker);
+ criticalEventLog, auxiliaryTaskExecutor, firstPidFilePromise, latencyTracker);
if (isMonitorCpuUsage()) {
// Wait for the first call to finish
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 663121e..4defdc6 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -249,6 +249,7 @@
final String mCallingProcessName;
final Intent intent;
final NeededUriGrants neededGrants;
+ final @Nullable String mCallingPackageName;
long deliveredTime;
int deliveryCount;
int doneExecutingCount;
@@ -258,7 +259,7 @@
StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id,
Intent _intent, NeededUriGrants _neededGrants, int _callingId,
- String callingProcessName) {
+ String callingProcessName, @Nullable String callingPackageName) {
sr = _sr;
taskRemoved = _taskRemoved;
id = _id;
@@ -266,6 +267,7 @@
neededGrants = _neededGrants;
callingId = _callingId;
mCallingProcessName = callingProcessName;
+ mCallingPackageName = callingPackageName;
}
UriPermissionOwner getUriPermissionsLocked() {
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index 9373328..2e99e97 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -41,6 +41,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -50,6 +51,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
@@ -65,11 +68,16 @@
new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
static final String ANR_FILE_PREFIX = "anr_";
- public static final String ANR_TRACE_DIR = "/data/anr";
+ static final String ANR_TEMP_FILE_PREFIX = "temp_anr_";
+ public static final String ANR_TRACE_DIR = "/data/anr";
private static final int NATIVE_DUMP_TIMEOUT_MS =
2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
+ // The time limit for a single process's dump
+ private static final int TEMP_DUMP_TIME_LIMIT =
+ 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 10 seconds
+
/**
* If a stack trace dump file is configured, dump process stack traces.
@@ -85,7 +93,8 @@
Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
@NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
- logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, latencyTracker);
+ logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, null,
+ latencyTracker);
}
/**
@@ -99,7 +108,7 @@
AnrLatencyTracker latencyTracker) {
return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
logExceptionCreatingFile, null, subject, criticalEventSection,
- auxiliaryTaskExecutor, latencyTracker);
+ auxiliaryTaskExecutor, null, latencyTracker);
}
/**
@@ -110,7 +119,8 @@
ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
- @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
+ @NonNull Executor auxiliaryTaskExecutor, Future<File> firstPidFilePromise,
+ AnrLatencyTracker latencyTracker) {
try {
if (latencyTracker != null) {
@@ -158,7 +168,7 @@
long firstPidEndPos = dumpStackTraces(
tracesFile.getAbsolutePath(), firstPids, nativePidsFuture,
- extraPidsFuture, latencyTracker);
+ extraPidsFuture, firstPidFilePromise, latencyTracker);
if (firstPidEndOffset != null) {
firstPidEndOffset.set(firstPidEndPos);
}
@@ -172,7 +182,6 @@
latencyTracker.dumpStackTracesEnded();
}
}
-
}
/**
@@ -180,7 +189,8 @@
*/
public static long dumpStackTraces(String tracesFile,
ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture,
- Future<ArrayList<Integer>> extraPidsFuture, AnrLatencyTracker latencyTracker) {
+ Future<ArrayList<Integer>> extraPidsFuture, Future<File> firstPidFilePromise,
+ AnrLatencyTracker latencyTracker) {
Slog.i(TAG, "Dumping to " + tracesFile);
@@ -191,33 +201,52 @@
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
- // As applications are usually interested with the ANR stack traces, but we can't share with
- // them the stack traces other than their own stacks. So after the very first PID is
+ // As applications are usually interested with the ANR stack traces, but we can't share
+ // with them the stack traces other than their own stacks. So after the very first PID is
// dumped, remember the current file size.
long firstPidEnd = -1;
- // First collect all of the stacks of the most important pids.
- if (firstPids != null) {
+ // Was the first pid copied from the temporary file that was created in the predump phase?
+ boolean firstPidTempDumpCopied = false;
+
+ // First copy the first pid's dump from the temporary file it was dumped into earlier,
+ // The first pid should always exist in firstPids but we check the size just in case.
+ if (firstPidFilePromise != null && firstPids != null && firstPids.size() > 0) {
+ final int primaryPid = firstPids.get(0);
+ final long start = SystemClock.elapsedRealtime();
+ firstPidTempDumpCopied = copyFirstPidTempDump(tracesFile, firstPidFilePromise,
+ remainingTime, latencyTracker);
+ final long timeTaken = SystemClock.elapsedRealtime() - start;
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (currently copying primary pid" + primaryPid
+ + "); deadline exceeded.");
+ return firstPidEnd;
+ }
+ // We don't copy ANR traces from the system_server intentionally.
+ if (firstPidTempDumpCopied && primaryPid != ActivityManagerService.MY_PID) {
+ firstPidEnd = new File(tracesFile).length();
+ }
+ // Append the Durations/latency comma separated array after the first PID.
+ if (latencyTracker != null) {
+ appendtoANRFile(tracesFile,
+ latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
+ }
+ }
+ // Next collect all of the stacks of the most important pids.
+ if (firstPids != null) {
if (latencyTracker != null) {
latencyTracker.dumpingFirstPidsStarted();
}
int num = firstPids.size();
- for (int i = 0; i < num; i++) {
+ for (int i = firstPidTempDumpCopied ? 1 : 0; i < num; i++) {
final int pid = firstPids.get(i);
// We don't copy ANR traces from the system_server intentionally.
final boolean firstPid = i == 0 && ActivityManagerService.MY_PID != pid;
- if (latencyTracker != null) {
- latencyTracker.dumpingPidStarted(pid);
- }
-
Slog.i(TAG, "Collecting stacks for pid " + pid);
- final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
- remainingTime);
- if (latencyTracker != null) {
- latencyTracker.dumpingPidEnded();
- }
-
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime,
+ latencyTracker);
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
@@ -301,13 +330,8 @@
}
for (int pid : extraPids) {
Slog.i(TAG, "Collecting stacks for extra pid " + pid);
- if (latencyTracker != null) {
- latencyTracker.dumpingPidStarted(pid);
- }
- final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
- if (latencyTracker != null) {
- latencyTracker.dumpingPidEnded();
- }
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime,
+ latencyTracker);
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid
@@ -330,6 +354,99 @@
return firstPidEnd;
}
+ /**
+ * Dumps the supplied pid to a temporary file.
+ * @param pid the PID to be dumped
+ * @param latencyTracker the latency tracker instance of the current ANR.
+ */
+ public static File dumpStackTracesTempFile(int pid, AnrLatencyTracker latencyTracker) {
+ try {
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesTempFileStarted();
+ }
+
+ File tmpTracesFile;
+ try {
+ tmpTracesFile = File.createTempFile(ANR_TEMP_FILE_PREFIX, ".txt",
+ new File(ANR_TRACE_DIR));
+ Slog.d(TAG, "created ANR temporary file:" + tmpTracesFile.getAbsolutePath());
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception creating temporary ANR dump file:", e);
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesTempFileCreationFailed();
+ }
+ return null;
+ }
+
+ Slog.i(TAG, "Collecting stacks for pid " + pid + " into temporary file "
+ + tmpTracesFile.getName());
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tmpTracesFile.getAbsolutePath(),
+ TEMP_DUMP_TIME_LIMIT);
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
+ if (TEMP_DUMP_TIME_LIMIT <= timeTaken) {
+ Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid
+ + "); deadline exceeded.");
+ tmpTracesFile.delete();
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesTempFileTimedOut();
+ }
+ return null;
+ }
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms"
+ + " dumped into temporary file " + tmpTracesFile.getName());
+ }
+ return tmpTracesFile;
+ } finally {
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesTempFileEnded();
+ }
+ }
+ }
+
+ private static boolean copyFirstPidTempDump(String tracesFile, Future<File> firstPidFilePromise,
+ long timeLimitMs, AnrLatencyTracker latencyTracker) {
+
+ boolean copySucceeded = false;
+ try (FileOutputStream fos = new FileOutputStream(tracesFile, true)) {
+ if (latencyTracker != null) {
+ latencyTracker.copyingFirstPidStarted();
+ }
+ final File tempfile = firstPidFilePromise.get(timeLimitMs, TimeUnit.MILLISECONDS);
+ if (tempfile != null) {
+ Files.copy(tempfile.toPath(), fos);
+ // Delete the temporary first pid dump file
+ tempfile.delete();
+ copySucceeded = true;
+ return copySucceeded;
+ }
+ return false;
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "Failed to collect the first pid's predump to the main ANR file",
+ e.getCause());
+ return false;
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Interrupted while collecting the first pid's predump"
+ + " to the main ANR file", e);
+ return false;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read the first pid's predump file", e);
+ return false;
+ } catch (TimeoutException e) {
+ Slog.w(TAG, "Copying the first pid timed out", e);
+ return false;
+ } finally {
+ if (latencyTracker != null) {
+ latencyTracker.copyingFirstPidEnded(copySucceeded);
+ }
+ }
+ }
+
private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
final String formattedDate = ANR_FILE_DATE_FORMAT.format(new Date());
final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
@@ -406,6 +523,21 @@
Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
}
}
+
+ private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs,
+ AnrLatencyTracker latencyTracker) {
+ try {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
+ return dumpJavaTracesTombstoned(pid, fileName, timeoutMs);
+ } finally {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
+ }
+ }
+
/**
* Dump java traces for process {@code pid} to the specified file. If java trace dumping
* fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index e38e8c1..e39ac2b 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -311,6 +311,11 @@
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
+ ProcessRecord getProcessRecordByIndex(int idx) {
+ return mProcRecords.valueAt(idx);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
ProcessRecord getProcessInPackage(String packageName) {
for (int i = mProcRecords.size() - 1; i >= 0; i--) {
final ProcessRecord app = mProcRecords.valueAt(i);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 127a9d8b..4e687f4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1046,9 +1046,14 @@
mSfxHelper = new SoundEffectsHelper(mContext, playerBase -> ignorePlayerLogs(playerBase));
- final boolean headTrackingDefault = mContext.getResources().getBoolean(
+ final boolean binauralEnabledDefault = SystemProperties.getBoolean(
+ "ro.audio.spatializer_binaural_enabled_default", true);
+ final boolean transauralEnabledDefault = SystemProperties.getBoolean(
+ "ro.audio.spatializer_transaural_enabled_default", true);
+ final boolean headTrackingEnabledDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_spatial_audio_head_tracking_enabled_default);
- mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, headTrackingDefault);
+ mSpatializerHelper = new SpatializerHelper(this, mAudioSystem,
+ binauralEnabledDefault, transauralEnabledDefault, headTrackingEnabledDefault);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
@@ -7237,7 +7242,7 @@
super.setDeviceVolumeBehavior_enforcePermission();
// verify arguments
Objects.requireNonNull(device);
- AudioManager.enforceSettableVolumeBehavior(deviceVolumeBehavior);
+ AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 8f54e45..5edd434 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -172,13 +172,17 @@
// initialization
@SuppressWarnings("StaticAssignmentInConstructor")
SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa,
- boolean headTrackingEnabledByDefault) {
+ boolean binauralEnabledDefault,
+ boolean transauralEnabledDefault,
+ boolean headTrackingEnabledDefault) {
mAudioService = mother;
mASA = asa;
// "StaticAssignmentInConstructor" warning is suppressed as the SpatializerHelper being
// constructed here is the factory for SADeviceState, thus SADeviceState and its
// private static field sHeadTrackingEnabledDefault should never be accessed directly.
- SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledByDefault;
+ SADeviceState.sBinauralEnabledDefault = binauralEnabledDefault;
+ SADeviceState.sTransauralEnabledDefault = transauralEnabledDefault;
+ SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledDefault;
}
synchronized void init(boolean effectExpected, @Nullable String settings) {
@@ -1547,10 +1551,12 @@
}
/*package*/ static final class SADeviceState {
+ private static boolean sBinauralEnabledDefault = true;
+ private static boolean sTransauralEnabledDefault = true;
private static boolean sHeadTrackingEnabledDefault = false;
final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
final @NonNull String mDeviceAddress;
- boolean mEnabled = true; // by default, SA is enabled on any device
+ boolean mEnabled;
boolean mHasHeadTracker = false;
boolean mHeadTrackerEnabled;
static final String SETTING_FIELD_SEPARATOR = ",";
@@ -1566,6 +1572,12 @@
SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) {
mDeviceType = deviceType;
mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : "";
+ final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
+ mEnabled = spatMode == SpatializationMode.SPATIALIZER_BINAURAL
+ ? sBinauralEnabledDefault
+ : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL
+ ? sTransauralEnabledDefault
+ : false;
mHeadTrackerEnabled = sHeadTrackingEnabledDefault;
}
diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java
index ce68edbb..70d7bde 100644
--- a/services/core/java/com/android/server/cpu/CpuInfoReader.java
+++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java
@@ -52,11 +52,6 @@
private static final String POLICY_DIR_PREFIX = "policy";
private static final String RELATED_CPUS_FILE = "related_cpus";
private static final String AFFECTED_CPUS_FILE = "affected_cpus";
- // TODO(b/263154344): Avoid reading from cpuinfo_cur_freq because non-root users don't have
- // read permission for this file. The file permissions are set by the Kernel. Instead, read
- // the current frequency only from scaling_cur_freq.
- private static final String CUR_CPUFREQ_FILE = "cpuinfo_cur_freq";
- private static final String MAX_CPUFREQ_FILE = "cpuinfo_max_freq";
private static final String CUR_SCALING_FREQ_FILE = "scaling_cur_freq";
private static final String MAX_SCALING_FREQ_FILE = "scaling_max_freq";
private static final String TIME_IN_STATE_FILE = "stats/time_in_state";
@@ -207,26 +202,16 @@
Slogf.w(TAG, "Missing dynamic policy info for policy ID %d", policyId);
continue;
}
- long curFreqKHz = CpuInfo.MISSING_FREQUENCY;
- long maxFreqKHz = CpuInfo.MISSING_FREQUENCY;
- if (dynamicPolicyInfo.curCpuFreqPair.cpuFreqKHz != CpuInfo.MISSING_FREQUENCY
- && staticPolicyInfo.maxCpuFreqPair.cpuFreqKHz != CpuInfo.MISSING_FREQUENCY) {
- curFreqKHz = dynamicPolicyInfo.curCpuFreqPair.cpuFreqKHz;
- maxFreqKHz = staticPolicyInfo.maxCpuFreqPair.cpuFreqKHz;
- } else if (dynamicPolicyInfo.curCpuFreqPair.scalingFreqKHz != CpuInfo.MISSING_FREQUENCY
- && staticPolicyInfo.maxCpuFreqPair.scalingFreqKHz
- != CpuInfo.MISSING_FREQUENCY) {
- curFreqKHz = dynamicPolicyInfo.curCpuFreqPair.scalingFreqKHz;
- maxFreqKHz = staticPolicyInfo.maxCpuFreqPair.scalingFreqKHz;
- } else {
+ if (dynamicPolicyInfo.curCpuFreqKHz == CpuInfo.MISSING_FREQUENCY
+ || staticPolicyInfo.maxCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) {
Slogf.w(TAG, "Current and maximum CPU frequency information mismatch/missing for"
+ " policy ID %d", policyId);
continue;
}
- if (curFreqKHz > maxFreqKHz) {
+ if (dynamicPolicyInfo.curCpuFreqKHz > staticPolicyInfo.maxCpuFreqKHz) {
Slogf.w(TAG, "Current CPU frequency (%d) is greater than maximum CPU frequency"
- + " (%d) for policy ID (%d). Skipping CPU frequency policy", curFreqKHz,
- maxFreqKHz, policyId);
+ + " (%d) for policy ID (%d). Skipping CPU frequency policy",
+ dynamicPolicyInfo.curCpuFreqKHz, staticPolicyInfo.maxCpuFreqKHz, policyId);
continue;
}
for (int coreIdx = 0; coreIdx < staticPolicyInfo.relatedCpuCores.size(); coreIdx++) {
@@ -249,7 +234,7 @@
if (dynamicPolicyInfo.affectedCpuCores.indexOf(relatedCpuCore) < 0) {
cpuInfoByCpus.append(relatedCpuCore, new CpuInfo(relatedCpuCore,
cpusetCategories, /* isOnline= */false, CpuInfo.MISSING_FREQUENCY,
- maxFreqKHz, CpuInfo.MISSING_FREQUENCY, usageStats));
+ staticPolicyInfo.maxCpuFreqKHz, CpuInfo.MISSING_FREQUENCY, usageStats));
continue;
}
// If a CPU core is online, it must have the usage stats. When the usage stats is
@@ -260,8 +245,8 @@
continue;
}
CpuInfo cpuInfo = new CpuInfo(relatedCpuCore, cpusetCategories, /* isOnline= */true,
- curFreqKHz, maxFreqKHz, dynamicPolicyInfo.avgTimeInStateCpuFreqKHz,
- usageStats);
+ dynamicPolicyInfo.curCpuFreqKHz, staticPolicyInfo.maxCpuFreqKHz,
+ dynamicPolicyInfo.avgTimeInStateCpuFreqKHz, usageStats);
cpuInfoByCpus.append(relatedCpuCore, cpuInfo);
if (DEBUG) {
Slogf.d(TAG, "Added %s for CPU core %d", cpuInfo, relatedCpuCore);
@@ -438,8 +423,8 @@
for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) {
int policyId = mCpuFreqPolicyDirsById.keyAt(i);
File policyDir = mCpuFreqPolicyDirsById.valueAt(i);
- FrequencyPair maxCpuFreqPair = readMaxCpuFrequency(policyDir);
- if (maxCpuFreqPair.isEmpty()) {
+ long maxCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE));
+ if (maxCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) {
Slogf.w(TAG, "Missing max CPU frequency information at %s",
policyDir.getAbsolutePath());
continue;
@@ -451,7 +436,7 @@
cpuCoresFile.getAbsolutePath());
continue;
}
- StaticPolicyInfo staticPolicyInfo = new StaticPolicyInfo(maxCpuFreqPair,
+ StaticPolicyInfo staticPolicyInfo = new StaticPolicyInfo(maxCpuFreqKHz,
relatedCpuCores);
mStaticPolicyInfoById.append(policyId, staticPolicyInfo);
if (DEBUG) {
@@ -461,18 +446,13 @@
}
}
- private FrequencyPair readMaxCpuFrequency(File policyDir) {
- return new FrequencyPair(readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE)),
- readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE)));
- }
-
private SparseArray<DynamicPolicyInfo> readDynamicPolicyInfo() {
SparseArray<DynamicPolicyInfo> dynamicPolicyInfoById = new SparseArray<>();
for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) {
int policyId = mCpuFreqPolicyDirsById.keyAt(i);
File policyDir = mCpuFreqPolicyDirsById.valueAt(i);
- FrequencyPair curCpuFreqPair = readCurrentCpuFrequency(policyDir);
- if (curCpuFreqPair.isEmpty()) {
+ long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, CUR_SCALING_FREQ_FILE));
+ if (curCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) {
Slogf.w(TAG, "Missing current frequency information at %s",
policyDir.getAbsolutePath());
continue;
@@ -484,7 +464,7 @@
Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
continue;
}
- DynamicPolicyInfo dynamicPolicyInfo = new DynamicPolicyInfo(curCpuFreqPair,
+ DynamicPolicyInfo dynamicPolicyInfo = new DynamicPolicyInfo(curCpuFreqKHz,
avgTimeInStateCpuFreqKHz, affectedCpuCores);
dynamicPolicyInfoById.append(policyId, dynamicPolicyInfo);
if (DEBUG) {
@@ -495,11 +475,6 @@
return dynamicPolicyInfoById;
}
- private FrequencyPair readCurrentCpuFrequency(File policyDir) {
- return new FrequencyPair(readCpuFreqKHz(new File(policyDir, CUR_CPUFREQ_FILE)),
- readCpuFreqKHz(new File(policyDir, CUR_SCALING_FREQ_FILE)));
- }
-
private long readAvgTimeInStateCpuFrequency(int policyId, File policyDir) {
LongSparseLongArray latestTimeInState = readTimeInState(policyDir);
if (latestTimeInState == null || latestTimeInState.size() == 0) {
@@ -913,58 +888,37 @@
}
}
- private static final class FrequencyPair {
- public final long cpuFreqKHz;
- public final long scalingFreqKHz;
-
- FrequencyPair(long cpuFreqKHz, long scalingFreqKHz) {
- this.cpuFreqKHz = cpuFreqKHz;
- this.scalingFreqKHz = scalingFreqKHz;
- }
-
- boolean isEmpty() {
- return cpuFreqKHz == CpuInfo.MISSING_FREQUENCY
- && scalingFreqKHz == CpuInfo.MISSING_FREQUENCY;
- }
-
- @Override
- public String toString() {
- return "FrequencyPair{cpuFreqKHz = " + cpuFreqKHz + ", scalingFreqKHz = "
- + scalingFreqKHz + '}';
- }
- }
-
private static final class StaticPolicyInfo {
- public final FrequencyPair maxCpuFreqPair;
+ public final long maxCpuFreqKHz;
public final IntArray relatedCpuCores;
- StaticPolicyInfo(FrequencyPair maxCpuFreqPair, IntArray relatedCpuCores) {
- this.maxCpuFreqPair = maxCpuFreqPair;
+ StaticPolicyInfo(long maxCpuFreqKHz, IntArray relatedCpuCores) {
+ this.maxCpuFreqKHz = maxCpuFreqKHz;
this.relatedCpuCores = relatedCpuCores;
}
@Override
public String toString() {
- return "StaticPolicyInfo{maxCpuFreqPair = " + maxCpuFreqPair + ", relatedCpuCores = "
+ return "StaticPolicyInfo{maxCpuFreqKHz = " + maxCpuFreqKHz + ", relatedCpuCores = "
+ relatedCpuCores + '}';
}
}
private static final class DynamicPolicyInfo {
- public final FrequencyPair curCpuFreqPair;
+ public final long curCpuFreqKHz;
public final long avgTimeInStateCpuFreqKHz;
public final IntArray affectedCpuCores;
- DynamicPolicyInfo(FrequencyPair curCpuFreqPair, long avgTimeInStateCpuFreqKHz,
+ DynamicPolicyInfo(long curCpuFreqKHz, long avgTimeInStateCpuFreqKHz,
IntArray affectedCpuCores) {
- this.curCpuFreqPair = curCpuFreqPair;
+ this.curCpuFreqKHz = curCpuFreqKHz;
this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz;
this.affectedCpuCores = affectedCpuCores;
}
@Override
public String toString() {
- return "DynamicPolicyInfo{curCpuFreqPair = " + curCpuFreqPair
+ return "DynamicPolicyInfo{curCpuFreqKHz = " + curCpuFreqKHz
+ ", avgTimeInStateCpuFreqKHz = " + avgTimeInStateCpuFreqKHz
+ ", affectedCpuCores = " + affectedCpuCores + '}';
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ea157c8..e01aa9b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1514,14 +1514,17 @@
}
}
- // When calling setContentRecordingSession into the WindowManagerService, the WMS
+ // When calling WindowManagerService#setContentRecordingSession, WindowManagerService
// attempts to acquire a lock before executing its main body. Due to this, we need
// to be sure that it isn't called while the DisplayManagerService is also holding
// a lock, to avoid a deadlock scenario.
final ContentRecordingSession session =
virtualDisplayConfig.getContentRecordingSession();
-
- if (displayId != Display.INVALID_DISPLAY && session != null) {
+ // Ensure session details are only set when mirroring (through VirtualDisplay flags or
+ // MediaProjection).
+ final boolean shouldMirror =
+ projection != null || (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0;
+ if (shouldMirror && displayId != Display.INVALID_DISPLAY && session != null) {
// Only attempt to set content recording session if there are details to set and a
// VirtualDisplay has been successfully constructed.
session.setDisplayId(displayId);
@@ -1529,8 +1532,8 @@
// We set the content recording session here on the server side instead of using
// a second AIDL call in MediaProjection. By ensuring that a virtual display has
// been constructed before calling setContentRecordingSession, we avoid a race
- // condition between the DMS & WMS which could lead to the MediaProjection
- // being pre-emptively torn down.
+ // condition between the DisplayManagerService & WindowManagerService which could
+ // lead to the MediaProjection being pre-emptively torn down.
if (!mWindowManagerInternal.setContentRecordingSession(session)) {
// Unable to start mirroring, so tear down projection & release VirtualDisplay.
try {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b336b95..7a0bf0c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2003,7 +2003,7 @@
@Override
public InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
@@ -2017,7 +2017,7 @@
public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId,
@DirectBootAwareness int directBootAwareness) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
@@ -2040,7 +2040,7 @@
@Override
public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
@@ -2062,7 +2062,7 @@
@Override
public boolean isStylusHandwritingAvailableAsUser(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
@@ -2158,7 +2158,8 @@
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
@@ -3634,7 +3635,8 @@
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
if (editorInfo == null || editorInfo.targetInputMethodUser == null
|| editorInfo.targetInputMethodUser.getIdentifier() != userId) {
@@ -4115,7 +4117,8 @@
@Override
public InputMethodSubtype getLastInputMethodSubtype(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
if (mSettings.getCurrentUserId() == userId) {
@@ -4133,7 +4136,8 @@
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
final int callingUid = Binder.getCallingUid();
@@ -4186,7 +4190,8 @@
public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
@NonNull int[] subtypeHashCodes, @UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
final int callingUid = Binder.getCallingUid();
final ComponentName imeComponentName =
@@ -5412,7 +5417,8 @@
@Override
public InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
synchronized (ImfLock.class) {
if (mSettings.getCurrentUserId() == userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 647a89e..6f0903c 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -25,6 +25,7 @@
import android.content.IntentFilter;
import android.telecom.TelecomManager;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.util.NotificationMessagingUtil;
import java.util.Comparator;
@@ -38,6 +39,7 @@
private final Context mContext;
private final NotificationMessagingUtil mMessagingUtil;
+ private final boolean mSortByInterruptiveness;
private String mDefaultPhoneApp;
public NotificationComparator(Context context) {
@@ -45,6 +47,8 @@
mContext.registerReceiver(mPhoneAppBroadcastReceiver,
new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
mMessagingUtil = new NotificationMessagingUtil(mContext);
+ mSortByInterruptiveness = !SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+ SystemUiSystemPropertiesFlags.NotificationFlags.NO_SORT_BY_INTERRUPTIVENESS);
}
@Override
@@ -135,10 +139,12 @@
return -1 * Integer.compare(leftPriority, rightPriority);
}
- final boolean leftInterruptive = left.isInterruptive();
- final boolean rightInterruptive = right.isInterruptive();
- if (leftInterruptive != rightInterruptive) {
- return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+ if (mSortByInterruptiveness) {
+ final boolean leftInterruptive = left.isInterruptive();
+ final boolean rightInterruptive = right.isInterruptive();
+ if (leftInterruptive != rightInterruptive) {
+ return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+ }
}
// then break ties by time, most recent first
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7fe6c7d..cd1ae74 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3279,7 +3279,7 @@
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
removePackageHelper.removePackage(stubPkg, true /*chatty*/);
try {
- return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null);
+ return initPackageTracedLI(scanFile, parseFlags, scanFlags);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -3410,8 +3410,7 @@
| ParsingPackageUtils.PARSE_MUST_BE_APK
| ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
@PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
- final AndroidPackage pkg = scanSystemPackageTracedLI(
- codePath, parseFlags, scanFlags, null);
+ final AndroidPackage pkg = initPackageTracedLI(codePath, parseFlags, scanFlags);
synchronized (mPm.mLock) {
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3591,7 +3590,7 @@
try {
final File codePath = new File(pkg.getPath());
synchronized (mPm.mInstallLock) {
- scanSystemPackageTracedLI(codePath, 0, scanFlags, null);
+ initPackageTracedLI(codePath, 0, scanFlags);
}
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
@@ -3734,12 +3733,6 @@
String errorMsg = null;
if (throwable == null) {
- // TODO(b/194319951): move lower in the scan chain
- // Static shared libraries have synthetic package names
- if (parseResult.parsedPackage.isStaticSharedLibrary()) {
- PackageManagerService.renameStaticSharedLibraryPackage(
- parseResult.parsedPackage);
- }
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
new UserHandle(UserHandle.USER_SYSTEM), apexInfo);
@@ -3804,8 +3797,8 @@
try {
synchronized (mPm.mInstallLock) {
- final AndroidPackage newPkg = scanSystemPackageTracedLI(
- scanFile, reparseFlags, rescanFlags, null);
+ final AndroidPackage newPkg = initPackageTracedLI(
+ scanFile, reparseFlags, rescanFlags);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
stubSystemApps.add(packageName);
@@ -3819,28 +3812,26 @@
}
/**
- * Traces a package scan.
- * @see #scanSystemPackageLI(File, int, int, UserHandle)
+ * Traces a package scan and registers it with the system.
+ * @see #initPackageLI(File, int, int)
*/
@GuardedBy("mPm.mInstallLock")
- public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
- int scanFlags, @Nullable ApexManager.ActiveApexInfo apexInfo)
+ public AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags)
throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
- return scanSystemPackageLI(scanFile, parseFlags, scanFlags, apexInfo);
+ return initPackageLI(scanFile, parseFlags, scanFlags);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
- * Scans a package and returns the newly parsed package.
+ * Scans a package, registers it with the system and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
@GuardedBy("mPm.mInstallLock")
- private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
- @Nullable ApexManager.ActiveApexInfo apexInfo)
+ private AndroidPackage initPackageLI(File scanFile, int parseFlags, int scanFlags)
throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
@@ -3852,13 +3843,8 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- // Static shared libraries have synthetic package names
- if (parsedPackage.isStaticSharedLibrary()) {
- PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
- }
-
return addForInitLI(parsedPackage, parseFlags, scanFlags,
- new UserHandle(UserHandle.USER_SYSTEM), apexInfo);
+ new UserHandle(UserHandle.USER_SYSTEM), null);
}
/**
@@ -3882,6 +3868,10 @@
throws PackageManagerException {
PackageSetting disabledPkgSetting;
synchronized (mPm.mLock) {
+ // Static shared libraries have synthetic package names
+ if (activeApexInfo == null && parsedPackage.isStaticSharedLibrary()) {
+ PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+ }
disabledPkgSetting =
mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
if (activeApexInfo != null && disabledPkgSetting != null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 47e0edf..a6d2b39 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -188,6 +188,7 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.permission.persistence.RuntimePermissionsPersistence;
+import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalManagerRegistry;
@@ -6747,6 +6748,16 @@
}
}
+ /**
+ * Read legacy permission states for permissions migration to new permission subsystem.
+ */
+ @Override
+ public RuntimePermissionsState getLegacyPermissionsState(int userId) {
+ synchronized (mLock) {
+ return mSettings.getLegacyPermissionsState(userId);
+ }
+ }
+
@Override
@SuppressWarnings("GuardedBy")
public boolean isPermissionUpgradeNeeded(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7703601..65e93fa 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3292,6 +3292,11 @@
mPackages, mSharedUsers, getUserRuntimePermissionsFile(userId));
}
+ RuntimePermissionsState getLegacyPermissionsState(@UserIdInt int userId) {
+ return mRuntimePermissionsPersistence.getLegacyPermissionsState(
+ userId, mPackages, mSharedUsers);
+ }
+
void applyDefaultPreferredAppsLPw(int userId) {
// First pull data from any pre-installed apps.
final PackageManagerInternal pmInternal =
@@ -5726,44 +5731,16 @@
Runnable writer = () -> {
boolean isLegacyPermissionStateStale = mIsLegacyPermissionStateStale.getAndSet(
false);
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions;
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions;
- final Map<String, List<RuntimePermissionsState.PermissionState>>
- packagePermissions = new ArrayMap<>();
- final Map<String, List<RuntimePermissionsState.PermissionState>>
- sharedUserPermissions = new ArrayMap<>();
synchronized (pmLock) {
if (sync || isLegacyPermissionStateStale) {
legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();
}
- int packagesSize = packageStates.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = packageStates.keyAt(i);
- PackageStateInternal packageState = packageStates.valueAt(i);
- if (!packageState.hasSharedUser()) {
- List<RuntimePermissionsState.PermissionState> permissions =
- getPermissionsFromPermissionsState(
- packageState.getLegacyPermissionState(), userId);
- if (permissions.isEmpty()
- && !packageState.isInstallPermissionsFixed()) {
- // Storing an empty state means the package is known to the
- // system and its install permissions have been granted and fixed.
- // If this is not the case, we should not store anything.
- continue;
- }
- packagePermissions.put(packageName, permissions);
- }
- }
-
- final int sharedUsersSize = sharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = sharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
- List<RuntimePermissionsState.PermissionState> permissions =
- getPermissionsFromPermissionsState(
- sharedUserSetting.getLegacyPermissionState(), userId);
- sharedUserPermissions.put(sharedUserName, permissions);
- }
+ packagePermissions = getPackagePermissions(userId, packageStates);
+ sharedUserPermissions = getShareUsersPermissions(userId, sharedUsers);
}
synchronized (mLock) {
int version = mVersions.get(userId, INITIAL_VERSION);
@@ -5791,6 +5768,68 @@
}
}
+ @NonNull
+ RuntimePermissionsState getLegacyPermissionsState(int userId,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
+ int version;
+ String fingerprint;
+ synchronized (mLock) {
+ version = mVersions.get(userId, INITIAL_VERSION);
+ fingerprint = mFingerprints.get(userId);
+ }
+
+ return new RuntimePermissionsState(
+ version, fingerprint, getPackagePermissions(userId, packageStates),
+ getShareUsersPermissions(userId, sharedUsers));
+ }
+
+ @NonNull
+ private Map<String, List<RuntimePermissionsState.PermissionState>> getPackagePermissions(
+ int userId,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates) {
+ final Map<String, List<RuntimePermissionsState.PermissionState>>
+ packagePermissions = new ArrayMap<>();
+
+ final int packagesSize = packageStates.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = packageStates.keyAt(i);
+ PackageStateInternal packageState = packageStates.valueAt(i);
+ if (!packageState.hasSharedUser()) {
+ List<RuntimePermissionsState.PermissionState> permissions =
+ getPermissionsFromPermissionsState(
+ packageState.getLegacyPermissionState(), userId);
+ if (permissions.isEmpty()
+ && !packageState.isInstallPermissionsFixed()) {
+ // Storing an empty state means the package is known to the
+ // system and its install permissions have been granted and fixed.
+ // If this is not the case, we should not store anything.
+ continue;
+ }
+ packagePermissions.put(packageName, permissions);
+ }
+ }
+ return packagePermissions;
+ }
+
+ @NonNull
+ private Map<String, List<RuntimePermissionsState.PermissionState>> getShareUsersPermissions(
+ int userId, @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
+ final Map<String, List<RuntimePermissionsState.PermissionState>>
+ sharedUserPermissions = new ArrayMap<>();
+
+ final int sharedUsersSize = sharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = sharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
+ List<RuntimePermissionsState.PermissionState> permissions =
+ getPermissionsFromPermissionsState(
+ sharedUserSetting.getLegacyPermissionState(), userId);
+ sharedUserPermissions.put(sharedUserName, permissions);
+ }
+ return sharedUserPermissions;
+ }
+
private void writePendingStates() {
while (true) {
final RuntimePermissionsState runtimePermissions;
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 7684a49..8f8f437 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -159,8 +159,8 @@
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
- pkg = installPackageHelper.scanSystemPackageTracedLI(
- ps.getPath(), parseFlags, SCAN_INITIAL, null);
+ pkg = installPackageHelper.initPackageTracedLI(
+ ps.getPath(), parseFlags, SCAN_INITIAL);
loaded.add(pkg);
} catch (PackageManagerException e) {
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 18eebe4..b4b8cb2 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -619,10 +619,10 @@
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
- extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
- null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
- null /* broadcastAllowList */,
+ extras, flags, null /* targetPkg */, null /* finishedReceiver */,
+ new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
mPm.snapshotComputer(), callingUid, intentExtras),
null /* bOptions */));
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8800d0a..c10820a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1275,7 +1275,7 @@
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
getDevicePolicyManagerInternal().broadcastIntentToManifestReceivers(
intent, parentHandle, /* requiresPermission= */ true);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, parentHandle);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
index 1282b6a..60ac0b0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
@@ -18,11 +18,9 @@
import android.annotation.NonNull;
import android.content.pm.PackageManagerInternal;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
-import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
@@ -81,15 +79,12 @@
*/
@NonNull
public Map<Integer, Map<String, LegacyPermissionState>> getLegacyPermissionStates(int userId) {
- RuntimePermissionsPersistence legacyPersistence =
- RuntimePermissionsPersistence.createInstance();
+ PackageManagerInternal mPackageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
Map<Integer, Map<String, LegacyPermissionState>> appIdPermissionStates = new ArrayMap<>();
- RuntimePermissionsState legacyState = legacyPersistence.readForUser(UserHandle.of(userId));
- if (legacyState == null) {
- return appIdPermissionStates;
- }
-
+ RuntimePermissionsState legacyState =
+ mPackageManagerInternal.getLegacyPermissionsState(userId);
PackageManagerLocal packageManagerLocal =
LocalManagerRegistry.getManager(PackageManagerLocal.class);
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index c0d71ac..579d4e3 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -26,10 +26,10 @@
]
},
{
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
},
{
"include-filter": "android.permission.cts.PermissionMaxSdkVersionTest"
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 501995c..2598758 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1572,7 +1572,9 @@
private void interceptScreenshotChord(int source, long pressDelay) {
mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source),
+ // arg2 is unused, but necessary to insure we call the correct method signature
+ // since the screenshot source is read from message.arg1
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source, 0),
pressDelay);
}
@@ -3596,16 +3598,16 @@
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
} else {
- setKeyguardOccludedLw(occluded, true /* notify */);
+ setKeyguardOccludedLw(occluded);
}
}
@Override
- public int applyKeyguardOcclusionChange(boolean notify) {
+ public int applyKeyguardOcclusionChange() {
if (mKeyguardOccludedChanged) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
+ mPendingKeyguardOccluded);
- if (setKeyguardOccludedLw(mPendingKeyguardOccluded, notify)) {
+ if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
}
}
@@ -3624,8 +3626,10 @@
*/
private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
boolean notifyOccluded) {
- final int redoLayout = applyKeyguardOcclusionChange(notifyOccluded);
- if (redoLayout != 0) return redoLayout;
+ if (notifyOccluded) {
+ final int redoLayout = applyKeyguardOcclusionChange();
+ if (redoLayout != 0) return redoLayout;
+ }
if (startKeyguardExitAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis());
@@ -3876,20 +3880,16 @@
}
/**
- * Updates the occluded state of the Keyguard.
+ * Updates the occluded state of the Keyguard immediately via
+ * {@link com.android.internal.policy.IKeyguardService}.
*
* @param isOccluded Whether the Keyguard is occluded by another window.
- * @param notify Notify keyguard occlude status change immediately via
- * {@link com.android.internal.policy.IKeyguardService}.
* @return Whether the flags have changed and we have to redo the layout.
*/
- private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
+ private boolean setKeyguardOccludedLw(boolean isOccluded) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
- if (isKeyguardOccluded() == isOccluded) {
- return false;
- }
- mKeyguardDelegate.setOccluded(isOccluded, notify);
+ mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
return mKeyguardDelegate.isShowing();
}
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 094e70f..9f1cb1a 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -29,16 +29,16 @@
]
},
{
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
},
{
- "include-filter": "android.permission2.cts.RestrictedStoragePermissionSharedUidTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedStoragePermissionSharedUidTest"
},
{
- "include-filter": "android.permission2.cts.RestrictedStoragePermissionTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedStoragePermissionTest"
}
]
},
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3c4dbf2..5d558e9 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -170,10 +170,10 @@
void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
/**
- * @param notify {@code true} if the status change should be immediately notified via
- * {@link com.android.internal.policy.IKeyguardService}
+ * Commit any queued changes to keyguard occlude status that had been deferred during the
+ * start of an animation or transition.
*/
- int applyKeyguardOcclusionChange(boolean notify);
+ int applyKeyguardOcclusionChange();
/**
* Interface to the Window Manager state associated with a particular
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 62932f9..f5bc8ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,6 +121,7 @@
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
+import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -5205,7 +5206,7 @@
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return;
}
- if (visible == mVisibleRequested && visible == mVisible
+ if (visible == mVisibleRequested && visible == mVisible && visible == isClientVisible()
&& mTransitionController.isShellTransitionsEnabled()) {
// For shell transition, it is no-op if there is no state change.
return;
@@ -9701,9 +9702,36 @@
return;
}
- if (getParent() != null) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ final Transition transition = new Transition(TRANSIT_RELAUNCH, 0 /* flags */,
+ mTransitionController, mWmService.mSyncEngine);
+ final Runnable executeRestart = () -> {
+ if (mState != RESTARTING_PROCESS || !attachedToProcess()) {
+ transition.abort();
+ return;
+ }
+ // Request invisible so there will be a change after the activity is restarted
+ // to be visible.
+ setVisibleRequested(false);
+ transition.collect(this);
+ mTransitionController.requestStartTransition(transition, task,
+ null /* remoteTransition */, null /* displayChange */);
+ scheduleStopForRestartProcess();
+ };
+ if (mWmService.mSyncEngine.hasActiveSync()) {
+ mWmService.mSyncEngine.queueSyncSet(
+ () -> mTransitionController.moveToCollecting(transition), executeRestart);
+ } else {
+ mTransitionController.moveToCollecting(transition);
+ executeRestart.run();
+ }
+ } else {
startFreezingScreen();
+ scheduleStopForRestartProcess();
}
+ }
+
+ private void scheduleStopForRestartProcess() {
// The process will be killed until the activity reports stopped with saved state (see
// {@link ActivityTaskManagerService.activityStopped}).
try {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7b9cc6f..1c46dd6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5371,7 +5371,7 @@
if (app != null && app.getPid() > 0) {
ArrayList<Integer> firstPids = new ArrayList<Integer>();
firstPids.add(app.getPid());
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null, null);
+ dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null, null, null);
}
File lastTracesFile = null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a4d475f..1344788 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1228,7 +1228,8 @@
private void finishHoldScreenUpdate() {
final boolean hold = mTmpHoldScreenWindow != null;
if (hold && mTmpHoldScreenWindow != mHoldScreenWindow) {
- mHoldScreenWakeLock.setWorkSource(new WorkSource(mTmpHoldScreenWindow.mSession.mUid));
+ mHoldScreenWakeLock.setWorkSource(new WorkSource(mTmpHoldScreenWindow.mSession.mUid,
+ mTmpHoldScreenWindow.mSession.mPackageName));
}
mHoldScreenWindow = mTmpHoldScreenWindow;
mTmpHoldScreenWindow = null;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8c59548..8f40e79 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
@@ -296,8 +297,11 @@
// be applied using the SurfaceControl hierarchy from the Organizer. This means
// we need to make sure that these changes in crop are reflected in the input
// windows, and so ensure this flag is set so that the input crop always reflects
- // the surface hierarchy.
- useSurfaceBoundsAsTouchRegion = true;
+ // the surface hierarchy. However, we only want to set this when the client did
+ // not already provide a touchable region, so that we don't ignore the one provided.
+ if (w.mTouchableInsets != TOUCHABLE_INSETS_REGION) {
+ useSurfaceBoundsAsTouchRegion = true;
+ }
if (w.mAttrs.isModal()) {
TaskFragment parent = w.getTaskFragment();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 78ee6f9..7b10c63 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -116,7 +116,7 @@
private boolean mShowingAlertWindowNotificationAllowed;
private boolean mClientDead = false;
private float mLastReportedAnimatorScale;
- private String mPackageName;
+ protected String mPackageName;
private String mRelayoutTag;
private final InsetsSourceControl.Array mDummyControls = new InsetsSourceControl.Array();
final boolean mSetsUnrestrictedKeepClearAreas;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8c6de8e..68b2d0f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3361,6 +3361,8 @@
&& info.pictureInPictureParams.isLaunchIntoPip()
&& top.getLastParentBeforePip() != null)
? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
+ info.lastParentTaskIdBeforePip = top != null && top.getLastParentBeforePip() != null
+ ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays;
info.mTopActivityLocusId = top != null ? top.getLocusId() : null;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 362e1c8..ba49dd0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1481,9 +1481,9 @@
// then we have to notify KeyguardService directly. This can happen if there is
// another ongoing transition when the app changes occlusion OR if the app dies or
// is killed. Both of these are common during tests.
- final boolean notify = !(transit == TRANSIT_KEYGUARD_OCCLUDE
- || transit == TRANSIT_KEYGUARD_UNOCCLUDE);
- mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(notify);
+ if (transit != TRANSIT_KEYGUARD_OCCLUDE && transit != TRANSIT_KEYGUARD_UNOCCLUDE) {
+ mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index cf9f7eb..653b125 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -439,7 +439,11 @@
// multiple sync at the same time because it may cause conflict.
// Create a new transition when there is no active sync to collect the changes.
final Transition transition = mTransitionController.createTransition(type);
- applyTransaction(wct, -1 /* syncId */, transition, caller);
+ if (applyTransaction(wct, -1 /* syncId */, transition, caller)
+ == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
+ transition.abort();
+ return;
+ }
mTransitionController.requestStartTransition(transition, null /* startTask */,
null /* remoteTransition */, null /* displayChange */);
transition.setAllReady();
@@ -476,24 +480,26 @@
// calls startSyncSet.
() -> mTransitionController.moveToCollecting(nextTransition),
() -> {
- if (mTaskFragmentOrganizerController.isValidTransaction(wct)) {
- applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
+ if (mTaskFragmentOrganizerController.isValidTransaction(wct)
+ && (applyTransaction(wct, -1 /* syncId */, nextTransition, caller)
+ != TRANSACT_EFFECTS_NONE
+ || !nextTransition.mParticipants.isEmpty())) {
mTransitionController.requestStartTransition(nextTransition,
null /* startTask */, null /* remoteTransition */,
null /* displayChange */);
nextTransition.setAllReady();
- } else {
- nextTransition.abort();
+ return;
}
+ nextTransition.abort();
});
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
+ private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller) {
- applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
+ return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
}
/**
@@ -501,8 +507,9 @@
* @param transition A transition to collect changes into.
* @param caller Info about the calling process.
* @param finishTransition The transition that is currently being finished.
+ * @return The effects of the window container transaction.
*/
- private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
+ private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller,
@Nullable Transition finishTransition) {
int effects = TRANSACT_EFFECTS_NONE;
@@ -639,6 +646,7 @@
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.continueWindowLayout();
}
+ return effects;
}
private int applyChanges(@NonNull WindowContainer<?> container,
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 8f9b949..a15d6630 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.credentials;
+import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
import static android.Manifest.permission.LAUNCH_CREDENTIAL_SELECTOR;
import static android.content.Context.CREDENTIAL_SERVICE;
@@ -124,7 +125,8 @@
serviceInfos.forEach(
info -> {
services.add(
- new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
+ new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
+ info));
});
return services;
}
@@ -418,6 +420,7 @@
// Check privileged permissions
mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
}
+ enforcePermissionForAllowedProviders(request);
final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
@@ -447,6 +450,9 @@
// TODO(b/273308895): implement
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+ enforcePermissionForAllowedProviders(request);
+
return cancelTransport;
}
@@ -823,6 +829,17 @@
}
}
+ private void enforcePermissionForAllowedProviders(GetCredentialRequest request) {
+ boolean containsAllowedProviders = request.getCredentialOptions()
+ .stream()
+ .anyMatch(option -> option.getAllowedProviders() != null
+ && !option.getAllowedProviders().isEmpty());
+ if (containsAllowedProviders) {
+ mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS,
+ null);
+ }
+ }
+
private void enforceCallingPackage(String callingPackage, int callingUid) {
int packageUid;
PackageManager pm = mContext.createContextAsUser(
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 4e058a8..8082cdb 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -27,6 +27,7 @@
import android.credentials.IGetCredentialCallback;
import android.credentials.ui.ProviderData;
import android.credentials.ui.RequestInfo;
+import android.os.Binder;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
@@ -84,11 +85,12 @@
protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
mChosenProviderFinalPhaseMetric.setUiCallStartTimeNanoseconds(System.nanoTime());
try {
- mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
+ Binder.withCleanCallingIdentity(() ->
+ mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
- mRequestId, mClientRequest, mClientAppInfo.getPackageName()),
- providerDataList));
- } catch (RemoteException e) {
+ mRequestId, mClientRequest, mClientAppInfo.getPackageName()),
+ providerDataList)));
+ } catch (RuntimeException e) {
mChosenProviderFinalPhaseMetric.setUiReturned(false);
respondToClientWithErrorAndFinish(
GetCredentialException.TYPE_UNKNOWN, "Unable to instantiate selector");
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 9bc5998..95b0ff0 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -94,7 +94,8 @@
RemoteCredentialService remoteCredentialService) {
android.credentials.GetCredentialRequest filteredRequest =
filterOptions(providerInfo.getCapabilities(),
- getRequestSession.mClientRequest);
+ getRequestSession.mClientRequest,
+ providerInfo.getComponentName());
if (filteredRequest != null) {
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap =
new HashMap<>();
@@ -142,17 +143,19 @@
@Nullable
private static android.credentials.GetCredentialRequest filterOptions(
List<String> providerCapabilities,
- android.credentials.GetCredentialRequest clientRequest
+ android.credentials.GetCredentialRequest clientRequest,
+ ComponentName componentName
) {
List<CredentialOption> filteredOptions = new ArrayList<>();
for (CredentialOption option : clientRequest.getCredentialOptions()) {
- if (providerCapabilities.contains(option.getType())) {
+ if (providerCapabilities.contains(option.getType())
+ && isProviderAllowed(option, componentName)) {
Log.i(TAG, "In createProviderRequest - capability found : "
+ option.getType());
filteredOptions.add(option);
} else {
Log.i(TAG, "In createProviderRequest - capability not "
- + "found : " + option.getType());
+ + "found, or provider not allowed : " + option.getType());
}
}
if (!filteredOptions.isEmpty()) {
@@ -165,6 +168,16 @@
return null;
}
+ private static boolean isProviderAllowed(CredentialOption option, ComponentName componentName) {
+ if (!option.getAllowedProviders().isEmpty() && !option.getAllowedProviders().contains(
+ componentName)) {
+ Log.d(TAG, "Provider allow list specified but does not contain this provider: "
+ + componentName.flattenToString());
+ return false;
+ }
+ return true;
+ }
+
public ProviderGetSession(Context context,
CredentialProviderInfo info,
ProviderInternalCallback<GetCredentialResponse> callbacks,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 96076e1..7a01b1c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -225,6 +225,7 @@
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER;
+import static android.provider.DeviceConfig.NAMESPACE_TELEPHONY;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -426,6 +427,7 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -3324,7 +3326,7 @@
onLockSettingsReady();
loadAdminDataAsync();
mOwners.systemReady();
- if (isWorkProfileTelephonyFlagEnabled()) {
+ if (isWorkProfileTelephonyEnabled()) {
applyManagedSubscriptionsPolicyIfRequired();
}
break;
@@ -3534,26 +3536,21 @@
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
updatePermissionPolicyCache(userId);
updateAdminCanGrantSensorsPermissionCache(userId);
- final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
- boolean isManagedSubscription;
+ final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
synchronized (getLockObject()) {
ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
preferentialNetworkServiceConfigs = owner != null
? owner.mPreferentialNetworkServiceConfigs
: List.of(PreferentialNetworkServiceConfig.DEFAULT);
-
- isManagedSubscription = owner != null && owner.mManagedSubscriptionsPolicy != null
- && owner.mManagedSubscriptionsPolicy.getPolicyType()
- == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS;
}
updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
- if (isManagedSubscription) {
- String defaultDialerPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultDialer);
- String defaultSmsPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultSms);
+ if (isProfileOwnerOfOrganizationOwnedDevice(userId)
+ && getManagedSubscriptionsPolicy().getPolicyType()
+ == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+ String defaultDialerPackageName = getOemDefaultDialerPackage();
+ String defaultSmsPackageName = getOemDefaultSmsPackage();
updateDialerAndSmsManagedShortcutsOverrideCache(defaultDialerPackageName,
defaultSmsPackageName);
}
@@ -7640,7 +7637,7 @@
}
mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
- if (isWorkProfileTelephonyFlagEnabled()) {
+ if (isWorkProfileTelephonyEnabled()) {
clearManagedSubscriptionsPolicy();
clearLauncherShortcutOverrides();
updateTelephonyCrossProfileIntentFilters(parentId, UserHandle.USER_NULL, false);
@@ -10991,8 +10988,10 @@
synchronized (mSubscriptionsChangedListenerLock) {
pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
}
- pw.println(
- "Flag enable_work_profile_telephony : " + isWorkProfileTelephonyFlagEnabled());
+ pw.println("DPM Flag enable_work_profile_telephony : "
+ + isWorkProfileTelephonyDevicePolicyManagerFlagEnabled());
+ pw.println("Telephony Flag enable_work_profile_telephony : "
+ + isWorkProfileTelephonySubscriptionManagerFlagEnabled());
mHandler.post(() -> handleDump(pw));
dumpResources(pw);
@@ -22703,11 +22702,24 @@
DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
}
- private static boolean isWorkProfileTelephonyFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
- DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+ private boolean isWorkProfileTelephonyEnabled() {
+ return isWorkProfileTelephonyDevicePolicyManagerFlagEnabled()
+ && isWorkProfileTelephonySubscriptionManagerFlagEnabled();
+ }
+
+ private boolean isWorkProfileTelephonyDevicePolicyManagerFlagEnabled() {
+ return DeviceConfig.getBoolean(NAMESPACE_DEVICE_POLICY_MANAGER,
+ ENABLE_WORK_PROFILE_TELEPHONY_FLAG, DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+ }
+
+ private boolean isWorkProfileTelephonySubscriptionManagerFlagEnabled() {
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ return DeviceConfig.getBoolean(NAMESPACE_TELEPHONY, ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
+ false);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
@Override
@@ -22820,7 +22832,7 @@
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
- if (isWorkProfileTelephonyFlagEnabled()) {
+ if (isWorkProfileTelephonyEnabled()) {
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
@@ -22834,7 +22846,7 @@
@Override
public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
- if (!isWorkProfileTelephonyFlagEnabled()) {
+ if (!isWorkProfileTelephonyEnabled()) {
throw new UnsupportedOperationException("This api is not enabled");
}
CallerIdentity caller = getCallerIdentity();
@@ -22842,9 +22854,10 @@
"This policy can only be set by a profile owner on an organization-owned "
+ "device.");
+ int parentUserId = getProfileParentId(caller.getUserId());
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM) && !isAdminTestOnlyLocked(
+ if (hasUserSetupCompleted(parentUserId) && !isAdminTestOnlyLocked(
admin.info.getComponent(), caller.getUserId())) {
throw new IllegalStateException("Not allowed to apply this policy after setup");
}
@@ -22866,7 +22879,6 @@
if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
final long id = mInjector.binderClearCallingIdentity();
try {
- int parentUserId = getProfileParentId(caller.getUserId());
installOemDefaultDialerAndSmsApp(caller.getUserId());
updateTelephonyCrossProfileIntentFilters(parentUserId, caller.getUserId(), true);
} finally {
@@ -22877,10 +22889,8 @@
private void installOemDefaultDialerAndSmsApp(int targetUserId) {
try {
- String defaultDialerPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultDialer);
- String defaultSmsPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultSms);
+ String defaultDialerPackageName = getOemDefaultDialerPackage();
+ String defaultSmsPackageName = getOemDefaultSmsPackage();
if (defaultDialerPackageName != null) {
mIPackageManager.installExistingPackageAsUser(defaultDialerPackageName,
@@ -22906,6 +22916,15 @@
}
}
+ private String getOemDefaultDialerPackage() {
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ return telecomManager.getSystemDialerPackage();
+ }
+
+ private String getOemDefaultSmsPackage() {
+ return mContext.getString(R.string.config_defaultSms);
+ }
+
private void updateDialerAndSmsManagedShortcutsOverrideCache(
String defaultDialerPackageName, String defaultSmsPackageName) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index b58016e..84ced1f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -142,6 +142,12 @@
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
+ // The package info cache is the cache for package and permission information.
+ // Disable the package info and package permission caches locally but leave the
+ // checkPermission cache active.
+ PackageManager.invalidatePackageInfoCache();
+ PermissionManager.disablePackageNamePermissionCache();
+
handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true)
.apply { start() }
handler = Handler(handlerThread.looper)
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy0/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy0/cpuinfo_cur_freq
deleted file mode 100644
index 80b164d..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy0/cpuinfo_cur_freq
+++ /dev/null
@@ -1 +0,0 @@
-"1.23
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy0/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy0/cpuinfo_max_freq
deleted file mode 100644
index 582d8c8..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy0/cpuinfo_max_freq
+++ /dev/null
@@ -1 +0,0 @@
-+2.5
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/cpuinfo_max_freq
deleted file mode 100644
index 749fce6..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/cpuinfo_max_freq
+++ /dev/null
@@ -1 +0,0 @@
-1000000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/scaling_cur_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/cpuinfo_cur_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy1/scaling_cur_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy2/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy2/cpuinfo_cur_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy2/cpuinfo_cur_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy2/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy2/cpuinfo_max_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/corrupted_cpufreq/policy2/cpuinfo_max_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/cpuinfo_cur_freq
deleted file mode 100644
index dadd973..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/cpuinfo_cur_freq
+++ /dev/null
@@ -1 +0,0 @@
-1230000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/cpuinfo_max_freq
deleted file mode 100644
index a93d6f7..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/cpuinfo_max_freq
+++ /dev/null
@@ -1 +0,0 @@
-2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_cur_freq
index 573541a..dadd973 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_cur_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_cur_freq
@@ -1 +1 @@
-0
+1230000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_max_freq
index 573541a..a93d6f7 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_max_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy0/scaling_max_freq
@@ -1 +1 @@
-0
+2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/scaling_cur_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/cpuinfo_cur_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/scaling_cur_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/scaling_max_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/cpuinfo_max_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy1/scaling_max_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy2/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy2/cpuinfo_cur_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy2/cpuinfo_cur_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy2/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy2/cpuinfo_max_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state/policy2/cpuinfo_max_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/cpuinfo_cur_freq
deleted file mode 100644
index 749fce6..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/cpuinfo_cur_freq
+++ /dev/null
@@ -1 +0,0 @@
-1000000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/cpuinfo_max_freq
deleted file mode 100644
index a93d6f7..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/cpuinfo_max_freq
+++ /dev/null
@@ -1 +0,0 @@
-2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_cur_freq
index 573541a..749fce6 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_cur_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_cur_freq
@@ -1 +1 @@
-0
+1000000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq
index 573541a..a93d6f7 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq
@@ -1 +1 @@
-0
+2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_cur_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/cpuinfo_cur_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_cur_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_max_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/cpuinfo_max_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_max_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/cpuinfo_cur_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/cpuinfo_cur_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/cpuinfo_max_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/cpuinfo_max_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/cpuinfo_cur_freq
deleted file mode 100644
index dadd973..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/cpuinfo_cur_freq
+++ /dev/null
@@ -1 +0,0 @@
-1230000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/cpuinfo_max_freq
deleted file mode 100644
index a93d6f7..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/cpuinfo_max_freq
+++ /dev/null
@@ -1 +0,0 @@
-2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_cur_freq
index 573541a..dadd973 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_cur_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_cur_freq
@@ -1 +1 @@
-0
+1230000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_max_freq
index 573541a..a93d6f7 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_max_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy0/scaling_max_freq
@@ -1 +1 @@
-0
+2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/scaling_cur_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/cpuinfo_cur_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/scaling_cur_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/scaling_max_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/cpuinfo_max_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy1/scaling_max_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy2/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy2/cpuinfo_cur_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy2/cpuinfo_cur_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy2/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy2/cpuinfo_max_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state/policy2/cpuinfo_max_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/cpuinfo_cur_freq
deleted file mode 100644
index 749fce6..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/cpuinfo_cur_freq
+++ /dev/null
@@ -1 +0,0 @@
-1000000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/cpuinfo_max_freq
deleted file mode 100644
index a93d6f7..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/cpuinfo_max_freq
+++ /dev/null
@@ -1 +0,0 @@
-2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_cur_freq
index 573541a..749fce6 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_cur_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_cur_freq
@@ -1 +1 @@
-0
+1000000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_max_freq
index 573541a..a93d6f7 100644
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_max_freq
+++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy0/scaling_max_freq
@@ -1 +1 @@
-0
+2500000
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/scaling_cur_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/cpuinfo_cur_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/scaling_cur_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/scaling_max_freq
similarity index 100%
rename from services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/cpuinfo_max_freq
rename to services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy1/scaling_max_freq
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy2/cpuinfo_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy2/cpuinfo_cur_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy2/cpuinfo_cur_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy2/cpuinfo_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy2/cpuinfo_max_freq
deleted file mode 100644
index e69de29..0000000
--- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_without_time_in_state_2/policy2/cpuinfo_max_freq
+++ /dev/null
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 8a5d3a6..63e8e56 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -50,6 +50,7 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -1078,7 +1079,7 @@
eq(getUidForPackage(PACKAGE_GREEN)), anyInt(), eq(Intent.ACTION_TIME_TICK),
eq(BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST),
eq(BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD),
- anyLong(), anyLong(), anyLong(), anyInt()), times(1));
+ anyLong(), anyLong(), anyLong(), anyInt(), anyString(), anyString()), times(1));
}
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 2d4f5ca..bca39ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -1646,6 +1646,79 @@
}
/**
+ * Verify prioritized receivers work as expected with deferrable broadcast - broadcast to
+ * app in cached state should be deferred and the rest should be delivered as per the priority
+ * order.
+ */
+ @Test
+ public void testPrioritized_withDeferrableBroadcasts() throws Exception {
+ // Legacy stack doesn't support deferral
+ Assume.assumeTrue(mImpl == Impl.MODERN);
+
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+ final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
+ final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
+
+ receiverGreenApp.setCached(true);
+ receiverBlueApp.setCached(true);
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastOptions opts = BroadcastOptions.makeBasic()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
+ final List receivers = List.of(
+ makeRegisteredReceiver(callerApp, 10),
+ makeRegisteredReceiver(receiverGreenApp, 9),
+ makeRegisteredReceiver(receiverBlueApp, 8),
+ makeRegisteredReceiver(receiverYellowApp, 8),
+ makeRegisteredReceiver(receiverOrangeApp, 7)
+ );
+ enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, opts, receivers));
+ waitForIdle();
+
+ // Green ignored since it's in cached state
+ verifyScheduleRegisteredReceiver(never(), receiverGreenApp, timeTick);
+ // Blue ignored since it's in cached state
+ verifyScheduleRegisteredReceiver(never(), receiverBlueApp, timeTick);
+
+ final IApplicationThread redThread = mAms.getProcessRecordLocked(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED)).getThread();
+ final IApplicationThread yellowThread = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW)).getThread();
+ final IApplicationThread orangeThread = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
+ getUidForPackage(PACKAGE_ORANGE)).getThread();
+
+ // Verify apps that are not in cached state will receive the broadcast in the order
+ // we expect.
+ final InOrder inOrder = inOrder(redThread, yellowThread, orangeThread);
+ inOrder.verify(redThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+ inOrder.verify(yellowThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+ inOrder.verify(orangeThread).scheduleRegisteredReceiver(
+ any(), argThat(filterEqualsIgnoringComponent(timeTick)),
+ anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(),
+ eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
+
+ // Shift blue to be active and confirm that deferred broadcast is delivered
+ receiverBlueApp.setCached(false);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
+ waitForIdle();
+ verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
+
+ // Shift green to be active and confirm that deferred broadcast is delivered
+ receiverGreenApp.setCached(false);
+ mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+ waitForIdle();
+ verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
+ }
+
+ /**
* Verify that we handle replacing a pending broadcast.
*/
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 1c2dc34..51dcc03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -31,10 +31,13 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
@@ -45,18 +48,18 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
-import android.testing.TestableContext;
import android.util.FloatProperty;
import android.view.Display;
import android.view.DisplayInfo;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
-import com.android.server.ExtendedMockitoRule;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -69,12 +72,12 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -92,9 +95,11 @@
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private MockitoSession mSession;
private OffsettableClock mClock;
private TestLooper mTestLooper;
private Handler mHandler;
+ private Context mContextSpy;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
@@ -111,38 +116,40 @@
@Mock
private PowerManager mPowerManagerMock;
@Mock
+ private Resources mResourcesMock;
+ @Mock
private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
- @Rule
- public final TestableContext mContext = new TestableContext(
- InstrumentationRegistry.getInstrumentation().getContext());
-
- @Rule
- public final ExtendedMockitoRule mExtendedMockitoRule =
- new ExtendedMockitoRule.Builder(this)
- .setStrictness(Strictness.LENIENT)
- .spyStatic(SystemProperties.class)
- .spyStatic(BatteryStatsService.class)
- .build();
-
@Before
public void setUp() throws Exception {
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .spyStatic(LocalServices.class)
+ .spyStatic(BatteryStatsService.class)
+ .spyStatic(Settings.System.class)
+ .startMocking();
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mHandler = new Handler(mTestLooper.getLooper());
-
addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
- addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
- mCdsiMock);
- mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
SystemProperties.set(anyString(), any()));
+ doAnswer((Answer<ColorDisplayService.ColorDisplayServiceInternal>) invocationOnMock ->
+ mCdsiMock).when(() -> LocalServices.getService(
+ ColorDisplayService.ColorDisplayServiceInternal.class));
doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
+ doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() ->
+ Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt()));
setUpSensors();
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
@@ -150,8 +157,8 @@
@After
public void tearDown() {
+ mSession.finishMocking();
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
@Test
@@ -412,9 +419,11 @@
@Test
public void testDisplayBrightnessFollowers_AutomaticBrightness() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
final float brightness = 0.4f;
final float nits = 300;
final float ambientLux = 3000;
@@ -531,9 +540,11 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -564,14 +575,17 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
+ when(mResourcesMock.getBoolean(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
+ .thenReturn(true);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
@@ -599,9 +613,11 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -614,9 +630,11 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -669,9 +687,9 @@
public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
float brightness = 0.3f;
float nits = 500;
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
- true);
+ when(mResourcesMock.getBoolean(
+ com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
+ .thenReturn(true);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
@@ -791,7 +809,7 @@
setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
final DisplayPowerController2 dpc = new DisplayPowerController2(
- mContext, injector, mDisplayPowerCallbacksMock, mHandler,
+ mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
hbmMetadata, /* bootCompleted= */ false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 1bc1d10..0a1bf1c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -31,10 +31,13 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
@@ -45,18 +48,18 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
-import android.testing.TestableContext;
import android.util.FloatProperty;
import android.view.Display;
import android.view.DisplayInfo;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
-import com.android.server.ExtendedMockitoRule;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -69,12 +72,12 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -92,9 +95,11 @@
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private MockitoSession mSession;
private OffsettableClock mClock;
private TestLooper mTestLooper;
private Handler mHandler;
+ private Context mContextSpy;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
@@ -111,38 +116,41 @@
@Mock
private PowerManager mPowerManagerMock;
@Mock
+ private Resources mResourcesMock;
+ @Mock
private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
- @Rule
- public final TestableContext mContext = new TestableContext(
- InstrumentationRegistry.getInstrumentation().getContext());
-
- @Rule
- public final ExtendedMockitoRule mExtendedMockitoRule =
- new ExtendedMockitoRule.Builder(this)
- .setStrictness(Strictness.LENIENT)
- .spyStatic(SystemProperties.class)
- .spyStatic(BatteryStatsService.class)
- .build();
-
@Before
public void setUp() throws Exception {
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .spyStatic(LocalServices.class)
+ .spyStatic(BatteryStatsService.class)
+ .spyStatic(Settings.System.class)
+ .startMocking();
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mHandler = new Handler(mTestLooper.getLooper());
addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
- addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
- mCdsiMock);
- mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
SystemProperties.set(anyString(), any()));
+ doAnswer((Answer<ColorDisplayService.ColorDisplayServiceInternal>) invocationOnMock ->
+ mCdsiMock).when(() -> LocalServices.getService(
+ ColorDisplayService.ColorDisplayServiceInternal.class));
doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
+ doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() ->
+ Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt()));
setUpSensors();
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
@@ -150,8 +158,8 @@
@After
public void tearDown() {
+ mSession.finishMocking();
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
@Test
@@ -414,9 +422,11 @@
@Test
public void testDisplayBrightnessFollowers_AutomaticBrightness() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
final float brightness = 0.4f;
final float nits = 300;
final float ambientLux = 3000;
@@ -534,9 +544,11 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -567,14 +579,17 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
+ when(mResourcesMock.getBoolean(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
+ .thenReturn(true);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
@@ -602,9 +617,11 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -617,10 +634,11 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-
+ doAnswer((Answer<Integer>) invocationOnMock ->
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+ .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+ eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)));
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -673,10 +691,9 @@
public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
float brightness = 0.3f;
float nits = 500;
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
- true);
-
+ when(mResourcesMock.getBoolean(
+ com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
+ .thenReturn(true);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
@@ -795,7 +812,7 @@
setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
final DisplayPowerController dpc = new DisplayPowerController(
- mContext, injector, mDisplayPowerCallbacksMock, mHandler,
+ mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
hbmMetadata, /* bootCompleted= */ false);
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index 9578993..acdfee9 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -48,8 +49,10 @@
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
@@ -63,8 +66,9 @@
private AnrHelper mAnrHelper;
private ProcessRecord mAnrApp;
- private ExecutorService mExecutorService;
+ private ExecutorService mAuxExecutorService;
+ private Future<File> mEarlyDumpFuture;
@Rule
public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
@@ -91,9 +95,12 @@
return mServiceThreadRule.getThread().getThreadHandler();
}
}, mServiceThreadRule.getThread());
- mExecutorService = mock(ExecutorService.class);
+ mAuxExecutorService = mock(ExecutorService.class);
+ final ExecutorService earlyDumpExecutorService = mock(ExecutorService.class);
+ mEarlyDumpFuture = mock(Future.class);
+ doReturn(mEarlyDumpFuture).when(earlyDumpExecutorService).submit(any(Callable.class));
- mAnrHelper = new AnrHelper(service, mExecutorService);
+ mAnrHelper = new AnrHelper(service, mAuxExecutorService, earlyDumpExecutorService);
});
}
@@ -125,8 +132,8 @@
verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
- eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mExecutorService),
- eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/);
+ eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService),
+ eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture));
}
@Test
@@ -139,7 +146,7 @@
processingLatch.await();
return null;
}).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(),
- anyBoolean(), any(), any(), anyBoolean(), anyBoolean());
+ anyBoolean(), any(), any(), anyBoolean(), anyBoolean(), any());
final ApplicationInfo appInfo = new ApplicationInfo();
final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
"annotation");
@@ -162,7 +169,7 @@
processingLatch.countDown();
// There is only one ANR reported.
verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding(
- anyString(), any(), any(), any(), anyBoolean(), any(), eq(mExecutorService),
- anyBoolean(), anyBoolean());
+ anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService),
+ anyBoolean(), anyBoolean(), any());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index 6350e22..d92b9f8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -203,6 +203,6 @@
processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
false /* aboveSystem */, timeoutRecord, mExecutorService, false /* onlyDumpSelf */,
- false /*isContinuousAnr*/);
+ false /*isContinuousAnr*/, null);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index 8f07238..3ad24de 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -80,7 +80,9 @@
asAdapter = mMockAudioSystem;
}
mSpatHelper = new SpatializerHelper(mMockAudioService, asAdapter,
- false /*headTrackingEnabledByDefault*/);
+ true /*binauralEnabledDefault*/,
+ true /*transauralEnabledDefault*/,
+ false /*headTrackingEnabledDefault*/);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index aaabb28..4f74ef8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5002,6 +5002,8 @@
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TELEPHONY,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
// Even if the caller is the managed profile, the current user is the user 0
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
@@ -5064,6 +5066,8 @@
verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TELEPHONY,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 94d30bb..6d2ce7f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -34,7 +34,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -65,12 +67,14 @@
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
+import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
import android.os.Process;
+import android.os.RemoteException;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayCutout;
@@ -1024,11 +1028,14 @@
}
@Test
- public void testCreateVirtualDisplay_setContentRecordingSessionSuccess() throws Exception {
+ public void testCreateVirtualDisplay_setContentRecordingSessionSuccess()
+ throws RemoteException {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mMockWindowManagerInternal
.setContentRecordingSession(any(ContentRecordingSession.class)))
.thenReturn(true);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
@@ -1042,17 +1049,19 @@
DisplayManagerService.BinderService binderService = displayManager.new BinderService();
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
}
@Test
- public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws Exception {
+ public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws RemoteException {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mMockWindowManagerInternal
.setContentRecordingSession(any(ContentRecordingSession.class)))
.thenReturn(false);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
@@ -1066,11 +1075,96 @@
DisplayManagerService.BinderService binderService = displayManager.new BinderService();
final int displayId = binderService.createVirtualDisplay(builder.build(),
- mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
assertThat(displayId).isEqualTo(Display.INVALID_DISPLAY);
}
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_noProjection_noFlags() {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ // Set no flags for the VirtualDisplay.
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ // Pass in a null projection.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ // VirtualDisplay is created but not for mirroring.
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_noProjection_noMirroringFlag() {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+ // Set a non-mirroring flag for the VirtualDisplay.
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ // Pass in a null projection.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ // VirtualDisplay is created but not for mirroring.
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_projection_noMirroringFlag()
+ throws RemoteException {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockWindowManagerInternal
+ .setContentRecordingSession(any(ContentRecordingSession.class)))
+ .thenReturn(true);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
+
+ // Set no flags for the VirtualDisplay.
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ // Pass in a non-null projection.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
+
+ // VirtualDisplay is created for mirroring.
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ }
+
/**
* Tests that the virtual display is created with
* {@link VirtualDisplayConfig.Builder#setSurface(Surface)}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 86878c53..8487903 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -232,7 +232,6 @@
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
boolean mIsCharging;
- boolean mIsRestrictedBucketEnabled = true;
List<String> mNonIdleWhitelistApps = new ArrayList<>();
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
@@ -316,11 +315,6 @@
}
@Override
- boolean isRestrictedBucketEnabled() {
- return mIsRestrictedBucketEnabled;
- }
-
- @Override
File getDataSystemDirectory() {
return new File(getContext().getFilesDir(), Long.toString(sRandom.nextLong()));
}
@@ -1355,50 +1349,6 @@
@Test
@FlakyTest(bugId = 185169504)
- public void testRestrictedBucketDisabled() throws Exception {
- mInjector.mIsRestrictedBucketEnabled = false;
- // Get the controller to read the new value. Capturing the ContentObserver isn't possible
- // at the moment.
- mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
-
- // Nothing should be able to put it into the RESTRICTED bucket.
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_TIMEOUT);
- assertNotBucket(STANDBY_BUCKET_RESTRICTED);
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_PREDICTED);
- assertNotBucket(STANDBY_BUCKET_RESTRICTED);
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM);
- assertNotBucket(STANDBY_BUCKET_RESTRICTED);
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_USER);
- assertNotBucket(STANDBY_BUCKET_RESTRICTED);
- }
-
- @Test
- @FlakyTest(bugId = 185169504)
- public void testRestrictedBucket_EnabledToDisabled() throws Exception {
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- mInjector.mIsRestrictedBucketEnabled = false;
- // Get the controller to read the new value. Capturing the ContentObserver isn't possible
- // at the moment.
- mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-
- mController.checkIdleStates(USER_ID);
- assertNotBucket(STANDBY_BUCKET_RESTRICTED);
- }
-
- @Test
- @FlakyTest(bugId = 185169504)
public void testPredictionRaiseFromRestrictedTimeout_highBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 91d4f8f..d73a3b8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -15,9 +15,11 @@
*/
package com.android.server.notification;
-import static org.hamcrest.Matchers.contains;
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.NO_SORT_BY_INTERRUPTIVENESS;
+
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -43,13 +45,14 @@
import android.telecom.TelecomManager;
import android.test.suitebuilder.annotation.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.server.UiServiceTestCase;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -58,7 +61,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class NotificationComparatorTest extends UiServiceTestCase {
@Mock Context mContext;
@Mock TelecomManager mTm;
@@ -92,9 +95,24 @@
private NotificationRecord mRecordColorized;
private NotificationRecord mRecordColorizedCall;
+ @Parameterized.Parameters(name = "sortByInterruptiveness={0}")
+ public static Boolean[] getSortByInterruptiveness() {
+ return new Boolean[] { true, false };
+ }
+
+ @Parameterized.Parameter
+ public boolean mSortByInterruptiveness;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> {
+ if (flag.mSysPropKey.equals(NO_SORT_BY_INTERRUPTIVENESS.mSysPropKey)) {
+ return !mSortByInterruptiveness;
+ }
+ return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
+ };
+
int userId = UserHandle.myUserId();
when(mContext.getResources()).thenReturn(getContext().getResources());
@@ -126,7 +144,7 @@
new StatusBarNotification(callPkg,
callPkg, 1, "mRecordMinCallNonInterruptive", callUid, callUid,
nonInterruptiveNotif,
- new UserHandle(userId), "", 2000), getDefaultChannel());
+ new UserHandle(userId), "", 2001), getDefaultChannel());
mRecordMinCallNonInterruptive.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
mRecordMinCallNonInterruptive.setInterruptive(false);
@@ -228,7 +246,7 @@
.setColorized(true).setColor(Color.WHITE)
.build();
mRecordCheaterColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
- pkg2, 1, "cheater", uid2, uid2, n11, new UserHandle(userId),
+ pkg2, 1, "cheaterColorized", uid2, uid2, n11, new UserHandle(userId),
"", 9258), getDefaultChannel());
mRecordCheaterColorized.setSystemImportance(NotificationManager.IMPORTANCE_LOW);
@@ -262,6 +280,11 @@
mRecordColorizedCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
}
+ @After
+ public void tearDown() {
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+ }
+
@Test
public void testOrdering() {
final List<NotificationRecord> expected = new ArrayList<>();
@@ -281,8 +304,13 @@
expected.add(mNoMediaSessionMedia);
expected.add(mRecordCheater);
expected.add(mRecordCheaterColorized);
- expected.add(mRecordMinCall);
- expected.add(mRecordMinCallNonInterruptive);
+ if (mSortByInterruptiveness) {
+ expected.add(mRecordMinCall);
+ expected.add(mRecordMinCallNonInterruptive);
+ } else {
+ expected.add(mRecordMinCallNonInterruptive);
+ expected.add(mRecordMinCall);
+ }
List<NotificationRecord> actual = new ArrayList<>();
actual.addAll(expected);
@@ -290,14 +318,18 @@
Collections.sort(actual, new NotificationComparator(mContext));
- assertThat(actual, contains(expected.toArray()));
+ assertThat(actual).containsExactlyElementsIn(expected).inOrder();
}
@Test
public void testRankingScoreOverrides() {
NotificationComparator comp = new NotificationComparator(mContext);
NotificationRecord recordMinCallNonInterruptive = spy(mRecordMinCallNonInterruptive);
- assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) < 0);
+ if (mSortByInterruptiveness) {
+ assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) < 0);
+ } else {
+ assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) > 0);
+ }
when(recordMinCallNonInterruptive.getRankingScore()).thenReturn(1f);
assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) > 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1d0715a..2a2641e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -314,7 +314,7 @@
}
@Override
- public int applyKeyguardOcclusionChange(boolean notify) {
+ public int applyKeyguardOcclusionChange() {
return 0;
}
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 0325ba6..f9b76f4 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
@@ -255,15 +256,26 @@
* Show switch to managed profile dialog if subscription is associated with managed profile.
*
* @param context Context object
- * @param subId subscription id
+ * @param subId subscription id
+ * @param callingUid uid for the calling app
+ * @param callingPackage package name of the calling app
*/
- public static void showErrorIfSubscriptionAssociatedWithManagedProfile(Context context,
- int subId) {
+ public static void showSwitchToManagedProfileDialogIfAppropriate(Context context,
+ int subId, int callingUid, String callingPackage) {
if (!isSwitchToManagedProfileDialogFlagEnabled()) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
+ UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+ // We only want to show this dialog, while user actually trying to send the message from
+ // a messaging app, in other cases this dialog don't make sense.
+ if (!TelephonyUtils.isUidForeground(context, callingUid)
+ || !TelephonyUtils.isPackageSMSRoleHolderForUser(context, callingPackage,
+ callingUserHandle)) {
+ return;
+ }
+
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
UserHandle associatedUserHandle = subscriptionManager.getSubscriptionUserHandle(subId);
@@ -295,22 +307,25 @@
"enable_switch_to_managed_profile_dialog", false);
}
- /**
- * Check if the process with given uid is foreground.
- *
- * @param context context
- * @param uid the caller uid
- * @return true if the process with uid is foreground, false otherwise.
- */
- public static boolean isUidForeground(Context context, int uid) {
- final long token = Binder.clearCallingIdentity();
- try {
- ActivityManager am = context.getSystemService(ActivityManager.class);
- boolean result = am != null && am.getUidImportance(uid)
- == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
- return result;
- } finally {
- Binder.restoreCallingIdentity(token);
+ private static boolean isUidForeground(Context context, int uid) {
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ boolean result = am != null && am.getUidImportance(uid)
+ == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+ return result;
+ }
+
+ private static boolean isPackageSMSRoleHolderForUser(Context context, String callingPackage,
+ UserHandle user) {
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ final List<String> smsRoleHolder = roleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_SMS, user);
+
+ // ROLE_SMS is an exclusive role per user, so there would just be one entry in the
+ // retuned list if not empty
+ if (!smsRoleHolder.isEmpty() && callingPackage.equals(smsRoleHolder.get(0))) {
+ return true;
}
+ return false;
+
}
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index 7c794473..e8a8576 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +34,7 @@
/**
* @hide
*/
+ @UnsupportedAppUsage
public PointingInfo(float satelliteAzimuthDegrees, float satelliteElevationDegrees) {
mSatelliteAzimuthDegrees = satelliteAzimuthDegrees;
mSatelliteElevationDegrees = satelliteElevationDegrees;
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
index df80159..87c8db3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,6 +46,7 @@
/**
* @hide
*/
+ @UnsupportedAppUsage
public SatelliteCapabilities(Set<Integer> supportedRadioTechnologies,
boolean isPointingRequired, int maxBytesPerOutgoingDatagram) {
mSupportedRadioTechnologies = supportedRadioTechnologies == null
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagram.java b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
index d3cb8a0..44f56f3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagram.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@
/**
* @hide
*/
+ @UnsupportedAppUsage
public SatelliteDatagram(@NonNull byte[] data) {
mData = data;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index f237ada..d8a6faf 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.telephony.ILongConsumer;
@@ -36,6 +37,7 @@
* datagramId to Telephony. If the callback is not received within five minutes,
* Telephony will resend the datagram.
*/
+ @UnsupportedAppUsage
void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
int pendingCount, @NonNull ILongConsumer callback);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index e32566d..7d82fd8 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -82,6 +83,7 @@
* @param context The context the SatelliteManager belongs to.
* @hide
*/
+ @UnsupportedAppUsage
public SatelliteManager(@Nullable Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
@@ -127,6 +129,7 @@
* {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_SATELLITE_ENABLED = "satellite_enabled";
/**
@@ -134,6 +137,7 @@
* {@link #requestIsDemoModeEnabled(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_DEMO_MODE_ENABLED = "demo_mode_enabled";
/**
@@ -141,6 +145,7 @@
* {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported";
/**
@@ -148,6 +153,7 @@
* {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities";
/**
@@ -155,6 +161,7 @@
* {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned";
/**
@@ -162,6 +169,7 @@
* {@link #requestIsSatelliteCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_SATELLITE_COMMUNICATION_ALLOWED =
"satellite_communication_allowed";
@@ -170,6 +178,7 @@
* {@link #requestTimeForNextSatelliteVisibility(Executor, OutcomeReceiver)}.
* @hide
*/
+ @UnsupportedAppUsage
public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
/**
@@ -340,6 +349,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -382,6 +392,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -436,6 +447,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -488,6 +500,7 @@
*
* @throws IllegalStateException if the Telephony process is not currently available.
*/
+ @UnsupportedAppUsage
public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -541,6 +554,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -707,6 +721,7 @@
*/
public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2;
+ /** @hide */
@IntDef(prefix = "DATAGRAM_TYPE_", value = {
DATAGRAM_TYPE_UNKNOWN,
DATAGRAM_TYPE_SOS_MESSAGE,
@@ -732,6 +747,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void startSatelliteTransmissionUpdates(@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener,
@NonNull SatelliteTransmissionUpdateCallback callback) {
@@ -801,6 +817,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void stopSatelliteTransmissionUpdates(
@NonNull SatelliteTransmissionUpdateCallback callback,
@NonNull @CallbackExecutor Executor executor,
@@ -856,6 +873,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor,
@@ -895,7 +913,7 @@
* {@link SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean)}
* should report as deprovisioned.
* For provisioning satellite service, refer to
- * {@link #provisionSatelliteService(String, CancellationSignal, Executor, Consumer)}.
+ * {@link #provisionSatelliteService(String, String, CancellationSignal, Executor, Consumer)}
*
* @param token The token of the device/subscription to be deprovisioned.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
@@ -904,6 +922,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void deprovisionSatelliteService(@NonNull String token,
@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -943,6 +962,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
@SatelliteError public int registerForSatelliteProvisionStateChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteProvisionStateCallback callback) {
@@ -985,6 +1005,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void unregisterForSatelliteProvisionStateChanged(
@NonNull SatelliteProvisionStateCallback callback) {
Objects.requireNonNull(callback);
@@ -1023,6 +1044,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -1074,6 +1096,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
@SatelliteError public int registerForSatelliteModemStateChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteStateCallback callback) {
@@ -1113,6 +1136,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
Objects.requireNonNull(callback);
ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
@@ -1147,6 +1171,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
@SatelliteError public int registerForSatelliteDatagram(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteDatagramCallback callback) {
@@ -1190,6 +1215,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void unregisterForSatelliteDatagram(@NonNull SatelliteDatagramCallback callback) {
Objects.requireNonNull(callback);
ISatelliteDatagramCallback internalCallback =
@@ -1227,6 +1253,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void pollPendingSatelliteDatagrams(@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
@@ -1279,6 +1306,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void sendSatelliteDatagram(@DatagramType int datagramType,
@NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
@NonNull @CallbackExecutor Executor executor,
@@ -1324,6 +1352,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -1381,6 +1410,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @UnsupportedAppUsage
public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Duration, SatelliteException> callback) {
Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index a62eb8b..20875af 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -16,6 +16,8 @@
package android.telephony.satellite;
+import android.compat.annotation.UnsupportedAppUsage;
+
/**
* A callback class for monitoring satellite provision state change events.
*
@@ -28,5 +30,6 @@
* @param provisioned The new provision state. {@code true} means satellite is provisioned
* {@code false} means satellite is not provisioned.
*/
+ @UnsupportedAppUsage
void onSatelliteProvisionStateChanged(boolean provisioned);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
index d9ecaa3..3312e3a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
@@ -16,6 +16,8 @@
package android.telephony.satellite;
+import android.compat.annotation.UnsupportedAppUsage;
+
/**
* A callback class for monitoring satellite modem state change events.
*
@@ -26,5 +28,6 @@
* Called when satellite modem state changes.
* @param state The new satellite modem state.
*/
+ @UnsupportedAppUsage
void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index d4fe57a..17fff44 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A callback class for monitoring satellite position update and datagram transfer state change
@@ -30,6 +31,7 @@
*
* @param pointingInfo The pointing info containing the satellite location.
*/
+ @UnsupportedAppUsage
void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
/**
@@ -39,6 +41,7 @@
* @param sendPendingCount The number of datagrams that are currently being sent.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
+ @UnsupportedAppUsage
void onSendDatagramStateChanged(
@SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
@SatelliteManager.SatelliteError int errorCode);
@@ -50,6 +53,7 @@
* @param receivePendingCount The number of datagrams that are currently pending to be received.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
+ @UnsupportedAppUsage
void onReceiveDatagramStateChanged(
@SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
@SatelliteManager.SatelliteError int errorCode);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
new file mode 100644
index 0000000..d90b3ca
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationWarm(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index deff42a..82953ac 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -41,3 +41,17 @@
"mockito-target-extended-minus-junit4",
],
}
+
+java_test_host {
+ name: "frameworks-base-android-os-unittests",
+ srcs: [
+ ":framework-android-os-unit-testable-src",
+ "java/android/os/test/DdmSyncStateTest.java",
+ ],
+ static_libs: [
+ "junit",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/tests/utils/testutils/java/android/os/test/DdmSyncStateTest.java b/tests/utils/testutils/java/android/os/test/DdmSyncStateTest.java
new file mode 100644
index 0000000..8274ce4
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/DdmSyncStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.test;
+
+import android.os.DdmSyncState;
+import android.os.DdmSyncState.Stage;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test DdmSyncState, the Android app stage boot sync system for DDM Client.
+ */
+
+public class DdmSyncStateTest {
+
+ @Test
+ public void testNoCycle() {
+ DdmSyncState.reset();
+ try {
+ DdmSyncState.next(Stage.Attach);
+ DdmSyncState.next(Stage.Bind);
+ DdmSyncState.next(Stage.Named);
+ DdmSyncState.next(Stage.Debugger);
+ DdmSyncState.next(Stage.Running);
+
+ // Cycling back here which is not allowed
+ DdmSyncState.next(Stage.Attach);
+ Assert.fail("Going back to attach should have failed");
+ } catch (IllegalStateException ignored) {
+
+ }
+ }
+
+ @Test
+ public void testDebuggerFlow() {
+ DdmSyncState.reset();
+ DdmSyncState.next(Stage.Attach);
+ DdmSyncState.next(Stage.Bind);
+ DdmSyncState.next(Stage.Named);
+ DdmSyncState.next(Stage.Debugger);
+ DdmSyncState.next(Stage.Running);
+ Assert.assertEquals(Stage.Running, DdmSyncState.getStage());
+
+ }
+
+ @Test
+ public void testNoDebugFlow() {
+ DdmSyncState.reset();
+ DdmSyncState.next(Stage.Attach);
+ DdmSyncState.next(Stage.Bind);
+ DdmSyncState.next(Stage.Named);
+ // Notice how Stage.Debugger stage is skipped
+ DdmSyncState.next(Stage.Running);
+ Assert.assertEquals(Stage.Running, DdmSyncState.getStage());
+ }
+}