Merge "Restore work tile position after restore" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b8538fb..705a4df 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -26,6 +26,7 @@
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":android.server.app.flags-aconfig-java{.generated_srcjars}",
":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
":android.service.dreams.flags-aconfig-java{.generated_srcjars}",
":android.service.notification.flags-aconfig-java{.generated_srcjars}",
@@ -853,6 +854,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// App
+aconfig_declarations {
+ name: "android.server.app.flags-aconfig",
+ package: "android.server.app",
+ srcs: ["services/core/java/com/android/server/app/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.server.app.flags-aconfig-java",
+ aconfig_declarations: "android.server.app.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// WebView
aconfig_declarations {
name: "android.webkit.flags-aconfig",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 3409838..117faa2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -126,6 +126,12 @@
"exclude-annotation": "org.junit.Ignore"
}
]
+ },
+ {
+ "name": "vts_treble_vintf_framework_test"
+ },
+ {
+ "name": "vts_treble_vintf_vendor_test"
}
],
"postsubmit-ravenwood": [
diff --git a/api/Android.bp b/api/Android.bp
index 2b1cfcb..9029d25 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -31,12 +31,10 @@
"blueprint",
"soong",
"soong-android",
- "soong-bp2build",
"soong-genrule",
"soong-java",
],
srcs: ["api.go"],
- testSrcs: ["api_test.go"],
pluginFor: ["soong_build"],
}
diff --git a/api/api.go b/api/api.go
index 71b1e10..2668999 100644
--- a/api/api.go
+++ b/api/api.go
@@ -22,7 +22,6 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/bazel"
"android/soong/genrule"
"android/soong/java"
)
@@ -65,7 +64,6 @@
type CombinedApis struct {
android.ModuleBase
- android.BazelModuleBase
properties CombinedApisProperties
}
@@ -115,20 +113,6 @@
Previous_api *string
}
-type Bazel_module struct {
- Label *string
- Bp2build_available *bool
-}
-type bazelProperties struct {
- *Bazel_module
-}
-
-var bp2buildNotAvailable = bazelProperties{
- &Bazel_module{
- Bp2build_available: proptools.BoolPtr(false),
- },
-}
-
// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
type MergedTxtDefinition struct {
// "current.txt" or "removed.txt"
@@ -143,8 +127,6 @@
ModuleTag string
// public, system, module-lib or system-server
Scope string
- // True if there is a bp2build definition for this module
- Bp2buildDefined bool
}
func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
@@ -178,20 +160,7 @@
},
}
props.Visibility = []string{"//visibility:public"}
- bazelProps := bazelProperties{
- &Bazel_module{
- Bp2build_available: proptools.BoolPtr(false),
- },
- }
- if txt.Bp2buildDefined {
- moduleDir := ctx.ModuleDir()
- if moduleDir == android.Bp2BuildTopLevel {
- moduleDir = ""
- }
- label := fmt.Sprintf("//%s:%s", moduleDir, moduleName)
- bazelProps.Label = &label
- }
- ctx.CreateModule(genrule.GenRuleFactory, &props, &bazelProps)
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
}
func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
@@ -221,7 +190,7 @@
props := fgProps{}
props.Name = proptools.StringPtr(i.name)
props.Srcs = createSrcs(i.modules, i.tag)
- ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable)
+ ctx.CreateModule(android.FileGroupFactory, &props)
}
}
@@ -315,7 +284,7 @@
props.Name = proptools.StringPtr("all-modules-public-stubs-source")
props.Srcs = createSrcs(modules, "{.public.stubs.source}")
props.Visibility = []string{"//frameworks/base"}
- ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable)
+ ctx.CreateModule(android.FileGroupFactory, &props)
}
func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
@@ -323,43 +292,38 @@
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
distFilename := []string{"android.txt", "android-removed.txt"}
- bp2BuildDefined := []bool{true, false}
for i, f := range []string{"current.txt", "removed.txt"} {
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-" + f,
- Modules: bootclasspath,
- ModuleTag: "{.public" + tagSuffix[i],
- Scope: "public",
- Bp2buildDefined: bp2BuildDefined[i],
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.public" + tagSuffix[i],
+ Scope: "public",
})
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-system-" + f,
- Modules: bootclasspath,
- ModuleTag: "{.system" + tagSuffix[i],
- Scope: "system",
- Bp2buildDefined: bp2BuildDefined[i],
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-system-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.system" + tagSuffix[i],
+ Scope: "system",
})
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-module-lib-" + f,
- Modules: bootclasspath,
- ModuleTag: "{.module-lib" + tagSuffix[i],
- Scope: "module-lib",
- Bp2buildDefined: bp2BuildDefined[i],
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-module-lib-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.module-lib" + tagSuffix[i],
+ Scope: "module-lib",
})
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-system-server-" + f,
- Modules: system_server_classpath,
- ModuleTag: "{.system-server" + tagSuffix[i],
- Scope: "system-server",
- Bp2buildDefined: bp2BuildDefined[i],
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-system-server-" + f,
+ Modules: system_server_classpath,
+ ModuleTag: "{.system-server" + tagSuffix[i],
+ Scope: "system-server",
})
}
for _, txt := range textFiles {
@@ -446,55 +410,9 @@
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
- android.InitBazelModule(module)
return module
}
-type bazelCombinedApisAttributes struct {
- Scope bazel.StringAttribute
- Base bazel.LabelAttribute
- Deps bazel.LabelListAttribute
-}
-
-// combined_apis bp2build converter
-func (a *CombinedApis) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
- basePrefix := "non-updatable"
- scopeToSuffix := map[string]string{
- "public": "-current.txt",
- "system": "-system-current.txt",
- "module-lib": "-module-lib-current.txt",
- "system-server": "-system-server-current.txt",
- }
-
- for scopeName, suffix := range scopeToSuffix {
- name := a.Name() + suffix
-
- var scope bazel.StringAttribute
- scope.SetValue(scopeName)
-
- var base bazel.LabelAttribute
- base.SetValue(android.BazelLabelForModuleDepSingle(ctx, basePrefix+suffix))
-
- var deps bazel.LabelListAttribute
- classpath := a.properties.Bootclasspath
- if scopeName == "system-server" {
- classpath = a.properties.System_server_classpath
- }
- deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, classpath))
-
- attrs := bazelCombinedApisAttributes{
- Scope: scope,
- Base: base,
- Deps: deps,
- }
- props := bazel.BazelTargetModuleProperties{
- Rule_class: "merged_txts",
- Bzl_load_location: "//build/bazel/rules/java:merged_txts.bzl",
- }
- ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, &attrs)
- }
-}
-
// Various utility methods below.
// Creates an array of ":<m><tag>" for each m in <modules>.
diff --git a/api/api_test.go b/api/api_test.go
deleted file mode 100644
index 70f2162..0000000
--- a/api/api_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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 api
-
-import (
- "testing"
-
- "android/soong/android"
- "android/soong/bp2build"
- "android/soong/java"
-)
-
-func runCombinedApisTestCaseWithRegistrationCtxFunc(t *testing.T, tc bp2build.Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) {
- t.Helper()
- (&tc).ModuleTypeUnderTest = "combined_apis"
- (&tc).ModuleTypeUnderTestFactory = combinedApisModuleFactory
- bp2build.RunBp2BuildTestCase(t, registrationCtxFunc, tc)
-}
-
-func runCombinedApisTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) {
- t.Helper()
- runCombinedApisTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("java_defaults", java.DefaultsFactory)
- ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- })
-}
-
-func TestCombinedApisGeneral(t *testing.T) {
- runCombinedApisTestCase(t, bp2build.Bp2buildTestCase{
- Description: "combined_apis, general case",
- Blueprint: `combined_apis {
- name: "foo",
- bootclasspath: ["bcp"],
- system_server_classpath: ["ssc"],
-}
-
-java_sdk_library {
- name: "bcp",
- srcs: ["a.java", "b.java"],
- shared_library: false,
-}
-java_sdk_library {
- name: "ssc",
- srcs: ["a.java", "b.java"],
- shared_library: false,
-}
-filegroup {
- name: "non-updatable-current.txt",
- srcs: ["current.txt"],
-}
-filegroup {
- name: "non-updatable-system-current.txt",
- srcs: ["system-current.txt"],
-}
-filegroup {
- name: "non-updatable-module-lib-current.txt",
- srcs: ["system-removed.txt"],
-}
-filegroup {
- name: "non-updatable-system-server-current.txt",
- srcs: ["system-lint-baseline.txt"],
-}
-`,
- Filesystem: map[string]string{
- "a/Android.bp": `
- java_defaults {
- name: "android.jar_defaults",
- }
- `,
- "api/current.txt": "",
- "api/removed.txt": "",
- "api/system-current.txt": "",
- "api/system-removed.txt": "",
- "api/test-current.txt": "",
- "api/test-removed.txt": "",
- },
- StubbedBuildDefinitions: []string{"bcp", "ssc", "non-updatable-current.txt", "non-updatable-system-current.txt", "non-updatable-module-lib-current.txt", "non-updatable-system-server-current.txt"},
- ExpectedHandcraftedModules: []string{"foo-current.txt", "foo-system-current.txt", "foo-module-lib-current.txt", "foo-system-server-current.txt"},
- ExpectedBazelTargets: []string{
- bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-current.txt", bp2build.AttrNameToString{
- "scope": `"public"`,
- "base": `":non-updatable-current.txt"`,
- "deps": `[":bcp"]`,
- }),
- bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-current.txt", bp2build.AttrNameToString{
- "scope": `"system"`,
- "base": `":non-updatable-system-current.txt"`,
- "deps": `[":bcp"]`,
- }),
- bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-module-lib-current.txt", bp2build.AttrNameToString{
- "scope": `"module-lib"`,
- "base": `":non-updatable-module-lib-current.txt"`,
- "deps": `[":bcp"]`,
- }),
- bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-server-current.txt", bp2build.AttrNameToString{
- "scope": `"system-server"`,
- "base": `":non-updatable-system-server-current.txt"`,
- "deps": `[":ssc"]`,
- }),
- },
- })
-}
diff --git a/api/go.mod b/api/go.mod
index f8bb1c0..445a6f4 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -6,7 +6,5 @@
android/soong v0.0.0
github.com/google/blueprint v0.0.0
google.golang.org/protobuf v0.0.0
- prebuilts/bazel/common/proto/analysis_v2 v0.0.0
- prebuilts/bazel/common/proto/build v0.0.0
go.starlark.net v0.0.0
)
diff --git a/api/go.work b/api/go.work
index aa2d2b1..edd002e 100644
--- a/api/go.work
+++ b/api/go.work
@@ -13,7 +13,5 @@
google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf
github.com/google/blueprint v0.0.0 => ../../../build/blueprint
github.com/google/go-cmp v0.0.0 => ../../../external/go-cmp
- prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../../prebuilts/bazel/common/proto/analysis_v2
- prebuilts/bazel/common/proto/build v0.0.0 => ../../../prebuilts/bazel/common/proto/build
go.starlark.net v0.0.0 => ../../../external/starlark-go
)
diff --git a/core/api/current.txt b/core/api/current.txt
index d490c3f..83e3fab 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -39071,6 +39071,7 @@
public class NetworkSecurityPolicy {
method public static android.security.NetworkSecurityPolicy getInstance();
+ method @FlaggedApi("android.security.certificate_transparency_configuration") public boolean isCertificateTransparencyVerificationRequired(@NonNull String);
method public boolean isCleartextTrafficPermitted();
method public boolean isCleartextTrafficPermitted(String);
}
@@ -44721,12 +44722,12 @@
field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
}
- public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
- ctor public PhoneNumberFormattingTextWatcher();
- ctor public PhoneNumberFormattingTextWatcher(String);
- method public void afterTextChanged(android.text.Editable);
- method public void beforeTextChanged(CharSequence, int, int, int);
- method public void onTextChanged(CharSequence, int, int, int);
+ @Deprecated public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
+ ctor @Deprecated public PhoneNumberFormattingTextWatcher();
+ ctor @Deprecated @WorkerThread public PhoneNumberFormattingTextWatcher(String);
+ method @Deprecated public void afterTextChanged(android.text.Editable);
+ method @Deprecated public void beforeTextChanged(CharSequence, int, int, int);
+ method @Deprecated public void onTextChanged(CharSequence, int, int, int);
}
public class PhoneNumberUtils {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9a65388..9f56933 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -656,6 +656,7 @@
field public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
+ field @FlaggedApi("android.view.contentprotection.flags.rapid_clear_notifications_by_listener_app_op_enabled") public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER = "android:rapid_clear_notifications_by_listener";
field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
@@ -14522,6 +14523,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void clearRadioPowerOffForReason(int);
method public void dial(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity();
+ method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableCellularIdentifierDisclosureNotifications(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
@@ -14594,6 +14596,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCellularIdentifierDisclosureNotificationEnabled();
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 39f2737..a3cd3dc 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2,6 +2,7 @@
package android {
public static final class Manifest.permission {
+ field @FlaggedApi("com.android.server.accessibility.motion_event_observing") public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
@@ -99,6 +100,8 @@
public class AccessibilityServiceInfo implements android.os.Parcelable {
method @NonNull public android.content.ComponentName getComponentName();
+ method @FlaggedApi("android.view.accessibility.motion_event_observing") public int getObservedMotionEventSources();
+ method @FlaggedApi("android.view.accessibility.motion_event_observing") public void setObservedMotionEventSources(int);
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 8ad6ea2..fc342fa 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -20,6 +20,7 @@
import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -53,6 +54,7 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.Flags;
import com.android.internal.R;
import com.android.internal.compat.IPlatformCompat;
@@ -630,7 +632,8 @@
InputDevice.SOURCE_TOUCH_NAVIGATION,
InputDevice.SOURCE_ROTARY_ENCODER,
InputDevice.SOURCE_JOYSTICK,
- InputDevice.SOURCE_SENSOR
+ InputDevice.SOURCE_SENSOR,
+ InputDevice.SOURCE_TOUCHSCREEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface MotionEventSources {}
@@ -642,6 +645,8 @@
@MotionEventSources
private int mMotionEventSources = 0;
+ private int mObservedMotionEventSources = 0;
+
/**
* Creates a new instance.
*/
@@ -817,6 +822,9 @@
mInteractiveUiTimeout = other.mInteractiveUiTimeout;
flags = other.flags;
mMotionEventSources = other.mMotionEventSources;
+ if (Flags.motionEventObserving()) {
+ setObservedMotionEventSources(other.mObservedMotionEventSources);
+ }
// NOTE: Ensure that only properties that are safe to be modified by the service itself
// are included here (regardless of hidden setters, etc.).
}
@@ -1024,16 +1032,75 @@
*/
public void setMotionEventSources(@MotionEventSources int motionEventSources) {
mMotionEventSources = motionEventSources;
+ mObservedMotionEventSources = 0;
+ }
+
+ /**
+ * Sets the bit mask of {@link android.view.InputDevice} sources that the accessibility service
+ * wants to observe generic {@link android.view.MotionEvent}s from if it has already requested
+ * to listen to them using {@link #setMotionEventSources(int)}. Events from these sources will
+ * be sent to the rest of the input pipeline without being consumed by accessibility services.
+ * This service will still be able to see them.
+ *
+ * <p><strong>Note:</strong> you will need to call this function every time you call {@link
+ * #setMotionEventSources(int)}. Calling {@link #setMotionEventSources(int)} clears the list of
+ * observed motion event sources for this service.
+ *
+ * <p><strong>Note:</strong> {@link android.view.InputDevice} sources contain source class bits
+ * that complicate bitwise flag removal operations. To remove a specific source you should
+ * rebuild the entire value using bitwise OR operations on the individual source constants.
+ *
+ * <p>Including an {@link android.view.InputDevice} source that does not send {@link
+ * android.view.MotionEvent}s is effectively a no-op for that source, since you will not receive
+ * any events from that source.
+ *
+ * <p><strong>Note:</strong> Calling this function with a source that has not been listened to
+ * using {@link #setMotionEventSources(int)} will throw an exception.
+ *
+ * @see AccessibilityService#onMotionEvent
+ * @see #MotionEventSources
+ * @see #setMotionEventSources(int)
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING)
+ @TestApi
+ public void setObservedMotionEventSources(int observedMotionEventSources) {
+ // Confirm that any sources requested here have already been requested for listening.
+ if ((observedMotionEventSources & ~mMotionEventSources) != 0) {
+ String message =
+ String.format(
+ "Requested motion event sources for listening = 0x%x but requested"
+ + " motion event sources for observing = 0x%x.",
+ mMotionEventSources, observedMotionEventSources);
+ throw new IllegalArgumentException(message);
+ }
+ mObservedMotionEventSources = observedMotionEventSources;
+ }
+
+ /**
+ * Returns the bit mask of {@link android.view.InputDevice} sources that the accessibility
+ * service wants to observe generic {@link android.view.MotionEvent}s from if it has already
+ * requested to listen to them using {@link #setMotionEventSources(int)}. Events from these
+ * sources will be sent to the rest of the input pipeline without being consumed by
+ * accessibility services. This service will still be able to see them.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING)
+ @MotionEventSources
+ @TestApi
+ public int getObservedMotionEventSources() {
+ return mObservedMotionEventSources;
}
/**
* The localized summary of the accessibility service.
- * <p>
- * <strong>Statically set from
- * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
- * </p>
- * @return The localized summary if available, and {@code null} if a summary
- * has not been provided.
+ *
+ * <p><strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
+ * meta-data}.</strong>
+ *
+ * @return The localized summary if available, and {@code null} if a summary has not been
+ * provided.
*/
public CharSequence loadSummary(PackageManager packageManager) {
if (mSummaryResId == 0) {
@@ -1260,6 +1327,7 @@
parcel.writeString(mTileServiceName);
parcel.writeInt(mIntroResId);
parcel.writeInt(mMotionEventSources);
+ parcel.writeInt(mObservedMotionEventSources);
}
private void initFromParcel(Parcel parcel) {
@@ -1285,6 +1353,8 @@
mTileServiceName = parcel.readString();
mIntroResId = parcel.readInt();
mMotionEventSources = parcel.readInt();
+ // use the setter here because it throws an exception for invalid values.
+ setObservedMotionEventSources(parcel.readInt());
}
@Override
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ffed405..4a9fa9e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6884,8 +6884,8 @@
* application package was involved.
*
* <p>If called while inside the handling of {@link #onNewIntent}, this function will
- * return the referrer that submitted that new intent to the activity. Otherwise, it
- * always returns the referrer of the original Intent.</p>
+ * return the referrer that submitted that new intent to the activity only after
+ * {@link #setIntent(Intent)} is called with the provided intent.</p>
*
* <p>Note that this is <em>not</em> a security feature -- you can not trust the
* referrer information, applications can spoof it.</p>
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ec43184..4b24b1f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -18,6 +18,7 @@
import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
+import static android.view.contentprotection.flags.Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED;
import static java.lang.Long.max;
@@ -1508,6 +1509,7 @@
*/
public static final int OP_CREATE_ACCESSIBILITY_OVERLAY =
AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY;
+
/**
* Indicate that the user has enabled or disabled mobile data
* @hide
@@ -1529,9 +1531,17 @@
*/
public static final int OP_RESERVED_FOR_TESTING = AppProtoEnums.APP_OP_RESERVED_FOR_TESTING;
+ /**
+ * Rapid clearing of notifications by a notification listener, see b/289080543 for details
+ *
+ * @hide
+ */
+ public static final int OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
+ AppProtoEnums.APP_OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 142;
+ public static final int _NUM_OP = 143;
/**
* All app ops represented as strings.
@@ -1680,6 +1690,7 @@
OPSTR_MEDIA_ROUTING_CONTROL,
OPSTR_ENABLE_MOBILE_DATA_BY_USER,
OPSTR_RESERVED_FOR_TESTING,
+ OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
})
public @interface AppOpString {}
@@ -2330,6 +2341,7 @@
@FlaggedApi(FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED)
public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY =
"android:create_accessibility_overlay";
+
/**
* Indicate that the user has enabled or disabled mobile data
* @hide
@@ -2350,6 +2362,16 @@
public static final String OPSTR_RESERVED_FOR_TESTING =
"android:reserved_for_testing";
+ /**
+ * Rapid clearing of notifications by a notification listener, see b/289080543 for details
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED)
+ public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
+ "android:rapid_clear_notifications_by_listener";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2909,6 +2931,10 @@
"ENABLE_MOBILE_DATA_BY_USER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
new AppOpInfo.Builder(OP_RESERVED_FOR_TESTING, OPSTR_RESERVED_FOR_TESTING,
"OP_RESERVED_FOR_TESTING").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
+ OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
+ "RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER")
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 9a818e4..bfec9430 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -52,4 +52,6 @@
void removeGameModeListener(IGameModeListener gameModeListener);
void addGameStateListener(IGameStateListener gameStateListener);
void removeGameStateListener(IGameStateListener gameStateListener);
+ @EnforcePermission("MANAGE_GAME_MODE")
+ void toggleGameDefaultFrameRate(boolean isEnabled);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 90a2659..4a6349b1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9381,7 +9381,7 @@
@Deprecated
@SystemApi
@RequiresPermission(MANAGE_DEVICE_ADMINS)
- public boolean setActiveProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName)
+ public boolean setActiveProfileOwner(@NonNull ComponentName admin, String ownerName)
throws IllegalArgumentException {
throwIfParentInstance("setActiveProfileOwner");
if (mService != null) {
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index e4a03c5..d5b5f40 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -37,6 +37,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
@@ -709,7 +710,9 @@
IntentSender intentSender = mService
.requestNotificationAccess(component, mContext.getUserId())
.getIntentSender();
- mContext.startIntentSender(intentSender, null, 0, 0, 0);
+ mContext.startIntentSender(intentSender, null, 0, 0, 0,
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (IntentSender.SendIntentException e) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e224329..4d75a51 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6302,6 +6302,11 @@
/**
* Check whether a particular package has been granted a particular
* permission.
+ * <p>
+ * <strong>Note: </strong>This API returns the underlying permission state
+ * as-is and is mostly intended for permission managing system apps. To
+ * perform an access check for a certain app, please use the
+ * {@link Context#checkPermission} APIs instead.
*
* @param permName The name of the permission you are checking for.
* @param packageName The name of the package you are checking against.
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 6c6b33b..57025c2 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -49,3 +49,10 @@
description: "Add support to unlock the private space using biometrics"
bug: "312184187"
}
+
+flag {
+ name: "support_autolock_for_private_space"
+ namespace: "profile_experiences"
+ description: "Add support to lock private space automatically after a time period"
+ bug: "303201022"
+}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 8196bf5..20b0932 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -40,7 +40,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -722,6 +721,7 @@
switch(format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
+ case ImageFormat.JPEG_R:
break;
default:
throw new IllegalArgumentException("Unsupported format: " + format);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 16ffaef..10a8022 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1657,6 +1657,7 @@
*/
public abstract CpuScalingPolicies getCpuScalingPolicies();
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public final static class HistoryTag {
public static final int HISTORY_TAG_POOL_OVERFLOW = -1;
@@ -1713,6 +1714,7 @@
* Optional detailed information that can go into a history step. This is typically
* generated each time the battery level changes.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public final static class HistoryStepDetails {
// Time (in 1/100 second) spent in user space and the kernel since the last step.
public int userTime;
@@ -1797,6 +1799,7 @@
/**
* An extension to the history item describing a proc state change for a UID.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class ProcessStateChange {
public int uid;
public @BatteryConsumer.ProcessState int processState;
@@ -1850,6 +1853,7 @@
/**
* Battery history record.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class HistoryItem {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public HistoryItem next;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 0ccc485..02e40cf 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -396,6 +396,7 @@
* This is Test API which will be used to override output of isDirectlyHandlingTransactionNative
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static void setIsDirectlyHandlingTransactionOverride(boolean isInTransaction) {
sIsHandlingBinderTransaction = isInTransaction;
}
@@ -1068,6 +1069,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable String getTransactionName(int transactionCode) {
return null;
}
@@ -1076,6 +1078,7 @@
* @hide
*/
@VisibleForTesting
+ @android.ravenwood.annotation.RavenwoodKeep
public final @Nullable String getTransactionTraceName(int transactionCode) {
final boolean isInterfaceUserDefined = getMaxTransactionId() == 0;
if (mTransactionTraceNames == null) {
@@ -1113,6 +1116,7 @@
return transactionTraceName;
}
+ @android.ravenwood.annotation.RavenwoodKeep
private @NonNull String getSimpleDescriptor() {
String descriptor = mDescriptor;
if (descriptor == null) {
@@ -1132,6 +1136,7 @@
* @return The highest user-defined transaction id of all transactions.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public int getMaxTransactionId() {
return 0;
}
diff --git a/core/java/android/os/Broadcaster.java b/core/java/android/os/Broadcaster.java
index 88760b0..17cf692 100644
--- a/core/java/android/os/Broadcaster.java
+++ b/core/java/android/os/Broadcaster.java
@@ -19,6 +19,7 @@
import android.compat.annotation.UnsupportedAppUsage;
/** @hide */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Broadcaster
{
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/os/BundleMerger.java b/core/java/android/os/BundleMerger.java
index 857aaf5..dc243a5 100644
--- a/core/java/android/os/BundleMerger.java
+++ b/core/java/android/os/BundleMerger.java
@@ -48,6 +48,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BundleMerger implements Parcelable {
private static final String TAG = "BundleMerger";
diff --git a/core/java/android/os/ConditionVariable.java b/core/java/android/os/ConditionVariable.java
index a13eaa6..b5ed53b 100644
--- a/core/java/android/os/ConditionVariable.java
+++ b/core/java/android/os/ConditionVariable.java
@@ -29,6 +29,7 @@
* This class uses itself as the object to wait on, so if you wait()
* or notify() on a ConditionVariable, the results are undefined.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ConditionVariable
{
private volatile boolean mCondition;
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 91b796a..7716055 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -218,6 +218,13 @@
@SystemApi
public static native int getPidIfSharable();
+ /**
+ * Return true if HIDL is supported on this device and false if not.
+ *
+ * @hide
+ */
+ public static native boolean isHidlSupported();
+
/** @hide */
public HidlSupport() {}
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index feed208..bc19655 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
import libcore.util.NativeAllocationRegistry;
@@ -78,6 +79,17 @@
String iface,
String serviceName)
throws RemoteException, NoSuchElementException {
+ if (!HidlSupport.isHidlSupported()
+ && (iface.equals("android.hidl.manager@1.0::IServiceManager")
+ || iface.equals("android.hidl.manager@1.1::IServiceManager")
+ || iface.equals("android.hidl.manager@1.2::IServiceManager"))) {
+ Log.i(
+ TAG,
+ "Replacing Java hwservicemanager with a fake HwNoService"
+ + " because HIDL is not supported on this device.");
+ return new HwNoService();
+ }
+
return getService(iface, serviceName, false /* retry */);
}
/**
diff --git a/core/java/android/os/HwNoService.java b/core/java/android/os/HwNoService.java
new file mode 100644
index 0000000..117c3ad
--- /dev/null
+++ b/core/java/android/os/HwNoService.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * A fake hwservicemanager that is used locally when HIDL isn't supported on the device.
+ *
+ * @hide
+ */
+final class HwNoService implements IHwBinder, IHwInterface {
+ /** @hide */
+ @Override
+ public void transact(int code, HwParcel request, HwParcel reply, int flags) {}
+
+ /** @hide */
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ return new HwNoService();
+ }
+
+ /** @hide */
+ @Override
+ public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+ return true;
+ }
+
+ /** @hide */
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient) {
+ return true;
+ }
+
+ /** @hide */
+ @Override
+ public IHwBinder asBinder() {
+ return this;
+ }
+}
diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java
index df99074..cbc4272 100644
--- a/core/java/android/os/PackageTagsList.java
+++ b/core/java/android/os/PackageTagsList.java
@@ -41,6 +41,7 @@
*/
@TestApi
@Immutable
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PackageTagsList implements Parcelable {
// an empty set value matches any attribution tag (ie, wildcard)
diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
index 3d8a550..e514d63 100644
--- a/core/java/android/os/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -35,6 +35,7 @@
* @param <T> the type of the value with an associated timestamp
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class TimestampedValue<T> implements Parcelable {
private final long mReferenceTimeMillis;
@Nullable
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index bc80c8b..6d4e284 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -23,6 +23,7 @@
* Currently the public representation of what a work source is not
* defined; this is an opaque container.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class WorkSource implements Parcelable {
static final String TAG = "WorkSource";
static final boolean DEBUG = false;
@@ -141,11 +142,17 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static boolean isChainedBatteryAttributionEnabled(Context context) {
return Settings.Global.getInt(context.getContentResolver(),
Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, 0) == 1;
}
+ /** @hide */
+ public static boolean isChainedBatteryAttributionEnabled$ravenwood(Context context) {
+ return false;
+ }
+
/**
* Returns the number of uids in this work source.
* @hide
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index bf22dcc..6941857 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -1,6 +1,6 @@
# Bug component: 95221
-# Please assign new bugs to android-storage-triage@, not to individual people
+# PLEASE ASSIGN NEW BUGS TO android-storage-triage@, NOT TO INDIVIDUAL PEOPLE
# Android Storage Team
alukin@google.com
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 69d86a6..437668c 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -44,3 +44,10 @@
description: "Enables the independent keyboard vibration settings feature"
bug: "289107579"
}
+
+flag {
+ namespace: "haptics"
+ name: "adaptive_haptics_enabled"
+ description: "Enables the adaptive haptics feature"
+ bug: "305961689"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2ed994b..2cc56d8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8301,6 +8301,17 @@
public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets";
/**
+ * Setting specifying the accessibility services, accessibility shortcut targets,
+ * or features to be toggled via a tile in the quick settings panel.
+ *
+ * <p> This is a colon-separated string list which contains the flattened
+ * {@link ComponentName} and the class name of a system class implementing a supported
+ * accessibility feature.
+ * @hide
+ */
+ public static final String ACCESSIBILITY_QS_TARGETS = "accessibility_qs_targets";
+
+ /**
* The system class name of magnification controller which is a target to be toggled via
* accessibility shortcut or accessibility button.
*
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 0c4eeda..e679d20 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -16,6 +16,8 @@
package android.security;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.security.net.config.ApplicationConfig;
@@ -26,9 +28,6 @@
*
* <p>Network stacks/components should honor this policy to make it possible to centrally control
* the relevant aspects of network security behavior.
- *
- * <p>The policy currently consists of a single flag: whether cleartext network traffic is
- * permitted. See {@link #isCleartextTrafficPermitted()}.
*/
public class NetworkSecurityPolicy {
@@ -94,6 +93,22 @@
}
/**
+ * Returns {@code true} if Certificate Transparency information is required to be verified by
+ * the client in TLS connections to {@code hostname}.
+ *
+ * <p>See RFC6962 section 3.3 for more details.
+ *
+ * @param hostname hostname to check whether certificate transparency verification is required
+ * @return {@code true} if certificate transparency verification is required and {@code false}
+ * otherwise
+ */
+ @FlaggedApi(Flags.FLAG_CERTIFICATE_TRANSPARENCY_CONFIGURATION)
+ public boolean isCertificateTransparencyVerificationRequired(@NonNull String hostname) {
+ return libcore.net.NetworkSecurityPolicy.getInstance()
+ .isCertificateTransparencyVerificationRequired(hostname);
+ }
+
+ /**
* Handle an update to the system or user certificate stores.
* @hide
*/
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 28ef70b..b56bef3 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.security"
flag {
+ name: "certificate_transparency_configuration"
+ namespace: "network_security"
+ description: "Enable certificate transparency setting in the network security config"
+ bug: "28746284"
+}
+
+flag {
name: "fsverity_api"
namespace: "hardware_backed_security"
description: "Feature flag for fs-verity API"
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 801eceb..4cc870b 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -16,10 +16,15 @@
package android.security.net.config;
+import static android.security.Flags.certificateTransparencyConfiguration;
+
+import android.annotation.NonNull;
import android.util.Pair;
+
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
+
import javax.net.ssl.X509TrustManager;
/**
@@ -147,6 +152,22 @@
return getConfigForHostname(hostname).isCleartextTrafficPermitted();
}
+ /**
+ * Returns {@code true} if Certificate Transparency information is required to be verified by
+ * the client in TLS connections to {@code hostname}.
+ *
+ * <p>See RFC6962 section 3.3 for more details.
+ *
+ * @param hostname hostname to check whether certificate transparency verification is required
+ * @return {@code true} if certificate transparency verification is required and {@code false}
+ * otherwise
+ */
+ public boolean isCertificateTransparencyVerificationRequired(@NonNull String hostname) {
+ return certificateTransparencyConfiguration()
+ ? getConfigForHostname(hostname).isCertificateTransparencyVerificationRequired()
+ : NetworkSecurityConfig.DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED;
+ }
+
public void handleTrustStorageUpdate() {
synchronized(mLock) {
// If the config is uninitialized then there is no work to be done to handle an update,
diff --git a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
index a708f5b..801b32b 100644
--- a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
+++ b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
@@ -40,6 +40,6 @@
@Override
public boolean isCertificateTransparencyVerificationRequired(String hostname) {
- return false;
+ return mConfig.isCertificateTransparencyVerificationRequired(hostname);
}
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 00872fb..129ae63 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -38,9 +38,12 @@
public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
/** @hide */
public static final boolean DEFAULT_HSTS_ENFORCED = false;
+ /** @hide */
+ public static final boolean DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED = false;
private final boolean mCleartextTrafficPermitted;
private final boolean mHstsEnforced;
+ private final boolean mCertificateTransparencyVerificationRequired;
private final PinSet mPins;
private final List<CertificatesEntryRef> mCertificatesEntryRefs;
private Set<TrustAnchor> mAnchors;
@@ -48,10 +51,15 @@
private NetworkSecurityTrustManager mTrustManager;
private final Object mTrustManagerLock = new Object();
- private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
- PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
+ private NetworkSecurityConfig(
+ boolean cleartextTrafficPermitted,
+ boolean hstsEnforced,
+ boolean certificateTransparencyVerificationRequired,
+ PinSet pins,
+ List<CertificatesEntryRef> certificatesEntryRefs) {
mCleartextTrafficPermitted = cleartextTrafficPermitted;
mHstsEnforced = hstsEnforced;
+ mCertificateTransparencyVerificationRequired = certificateTransparencyVerificationRequired;
mPins = pins;
mCertificatesEntryRefs = certificatesEntryRefs;
// Sort the certificates entry refs so that all entries that override pins come before
@@ -104,6 +112,11 @@
return mHstsEnforced;
}
+ // TODO(b/28746284): add exceptions for user-added certificates and enterprise overrides.
+ public boolean isCertificateTransparencyVerificationRequired() {
+ return mCertificateTransparencyVerificationRequired;
+ }
+
public PinSet getPins() {
return mPins;
}
@@ -208,6 +221,9 @@
private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
private boolean mCleartextTrafficPermittedSet = false;
private boolean mHstsEnforcedSet = false;
+ private boolean mCertificateTransparencyVerificationRequired =
+ DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED;
+ private boolean mCertificateTransparencyVerificationRequiredSet = false;
private Builder mParentBuilder;
/**
@@ -313,12 +329,35 @@
return mCertificatesEntryRefs;
}
+ Builder setCertificateTransparencyVerificationRequired(boolean required) {
+ mCertificateTransparencyVerificationRequired = required;
+ mCertificateTransparencyVerificationRequiredSet = true;
+ return this;
+ }
+
+ private boolean getCertificateTransparencyVerificationRequired() {
+ if (mCertificateTransparencyVerificationRequiredSet) {
+ return mCertificateTransparencyVerificationRequired;
+ }
+ if (mParentBuilder != null) {
+ return mParentBuilder.getCertificateTransparencyVerificationRequired();
+ }
+ return DEFAULT_CERTIFICATE_TRANSPARENCY_VERIFICATION_REQUIRED;
+ }
+
public NetworkSecurityConfig build() {
boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
boolean hstsEnforced = getEffectiveHstsEnforced();
+ boolean certificateTransparencyVerificationRequired =
+ getCertificateTransparencyVerificationRequired();
PinSet pinSet = getEffectivePinSet();
List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
- return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
+ return new NetworkSecurityConfig(
+ cleartextPermitted,
+ hstsEnforced,
+ certificateTransparencyVerificationRequired,
+ pinSet,
+ entryRefs);
}
}
}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index 311a8d2..b1c1479 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -171,6 +171,11 @@
return new Domain(domain, includeSubdomains);
}
+ private boolean parseCertificateTransparency(XmlResourceParser parser)
+ throws IOException, XmlPullParserException, ParserException {
+ return parser.getAttributeBooleanValue(null, "enabled", false);
+ }
+
private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser,
boolean defaultOverridePins)
throws IOException, XmlPullParserException, ParserException {
@@ -226,7 +231,6 @@
boolean seenPinSet = false;
boolean seenTrustAnchors = false;
boolean defaultOverridePins = configType == CONFIG_DEBUG;
- String configName = parser.getName();
int outerDepth = parser.getDepth();
// Add this builder now so that this builder occurs before any of its children. This
// makes the final build pass easier.
@@ -279,6 +283,15 @@
"Nested domain-config not allowed in " + getConfigString(configType));
}
builders.addAll(parseConfigEntry(parser, seenDomains, builder, configType));
+ } else if ("certificateTransparency".equals(tagName)) {
+ if (configType != CONFIG_BASE && configType != CONFIG_DOMAIN) {
+ throw new ParserException(
+ parser,
+ "certificateTransparency not allowed in "
+ + getConfigString(configType));
+ }
+ builder.setCertificateTransparencyVerificationRequired(
+ parseCertificateTransparency(parser));
} else {
XmlUtils.skipCurrentTag(parser);
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index c9526fd..3b444c4 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -75,7 +75,7 @@
private boolean mWindowShown;
// The reason that the last call to dispatchOnPreDraw() returned true to cancel and redraw
- private String mLastDispatchOnPreDrawCanceledReason;
+ private StringBuilder mLastDispatchOnPreDrawCanceledReason;
private boolean mAlive = true;
@@ -1173,9 +1173,15 @@
int count = access.size();
for (int i = 0; i < count; i++) {
final OnPreDrawListener preDrawListener = access.get(i);
- cancelDraw |= !(preDrawListener.onPreDraw());
- if (cancelDraw) {
- mLastDispatchOnPreDrawCanceledReason = preDrawListener.getClass().getName();
+ final boolean listenerCanceledDraw = !(preDrawListener.onPreDraw());
+ cancelDraw |= listenerCanceledDraw;
+ if (listenerCanceledDraw) {
+ final String className = preDrawListener.getClass().getName();
+ if (mLastDispatchOnPreDrawCanceledReason == null) {
+ mLastDispatchOnPreDrawCanceledReason = new StringBuilder(className);
+ } else {
+ mLastDispatchOnPreDrawCanceledReason.append("|").append(className);
+ }
}
}
} finally {
@@ -1191,7 +1197,10 @@
* @hide
*/
final String getLastDispatchOnPreDrawCanceledReason() {
- return mLastDispatchOnPreDrawCanceledReason;
+ if (mLastDispatchOnPreDrawCanceledReason != null) {
+ return mLastDispatchOnPreDrawCanceledReason.toString();
+ }
+ return null;
}
/**
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index e057660..0cc19fb 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -59,6 +59,13 @@
}
flag {
+ name: "motion_event_observing"
+ namespace: "accessibility"
+ description: "Allows accessibility services to intercept but not consume motion events from specified sources."
+ bug: "297595990"
+}
+
+flag {
namespace: "accessibility"
name: "granular_scrolling"
description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6bc2a13..bb49679 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -38,6 +38,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.app.ActivityOptions;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.ViewNodeBuilder;
import android.app.assist.AssistStructure.ViewNodeParcelable;
@@ -4370,7 +4371,11 @@
if (afm != null) {
afm.post(() -> {
try {
- afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
+ Bundle options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle();
+ afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0, options);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
}
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index f3dc33c..2a3008a 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -27,3 +27,10 @@
description: "If true, an appop is logged on creation of accessibility overlays."
bug: "289081465"
}
+
+flag {
+ name: "rapid_clear_notifications_by_listener_app_op_enabled"
+ namespace: "content_protection"
+ description: "If true, an appop is logged when a notification is rapidly cleared by a notification listener."
+ bug: "289080543"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 7f65c52..07beb11 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -16,7 +16,7 @@
flag {
name: "defer_display_updates"
- namespace: "window_manager"
+ namespace: "windowing_frontend"
description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
bug: "259220649"
is_fixed_read_only: true
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 7d78f29..0be9804 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -72,6 +72,7 @@
* All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by
* locks on BatteryStatsImpl object.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BatteryStatsHistory {
private static final boolean DEBUG = false;
private static final String TAG = "BatteryStatsHistory";
@@ -259,6 +260,7 @@
* until the first change occurs.
*/
@VisibleForTesting
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class TraceDelegate {
// Note: certain tests currently run as platform_app which is not allowed
// to set debug system properties. To ensure that system properties are set
@@ -391,10 +393,18 @@
public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
MonotonicClock monotonicClock) {
+ this(maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, monotonicClock,
+ new TraceDelegate());
+ }
+
+ @VisibleForTesting
+ public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
+ MonotonicClock monotonicClock, TraceDelegate traceDelegate) {
mMaxHistoryFiles = maxHistoryFiles;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
- mTracer = new TraceDelegate();
+ mTracer = traceDelegate;
mClock = clock;
mMonotonicClock = monotonicClock;
@@ -2096,6 +2106,7 @@
* fewer bytes. It is a bit more expensive than just writing the long into the parcel,
* but at scale saves a lot of storage and allows recording of longer battery history.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class VarintParceler {
/**
* Writes an array of longs into Parcel using the varint format, see
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 6bd5898..2dffe15 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -28,6 +28,7 @@
/**
* An iterator for {@link BatteryStats.HistoryItem}'s.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.HistoryItem>,
AutoCloseable {
private static final boolean DEBUG = false;
diff --git a/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java b/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java
index 7701761..00043cf 100644
--- a/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java
+++ b/core/java/com/android/internal/os/BinderCallHeavyHitterWatcher.java
@@ -33,6 +33,7 @@
* A watcher which makes stats on the incoming binder transaction, if the amount of some type of
* transactions exceeds the threshold, the listener will be notified.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BinderCallHeavyHitterWatcher {
private static final String TAG = "BinderCallHeavyHitterWatcher";
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
index 8ca6241..e7abe2a 100644
--- a/core/java/com/android/internal/os/BinderDeathDispatcher.java
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -36,6 +36,7 @@
*
* test with: atest FrameworksCoreTests:BinderDeathDispatcherTest
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BinderDeathDispatcher<T extends IInterface> {
private static final String TAG = "BinderDeathDispatcher";
diff --git a/core/java/com/android/internal/os/BinderLatencyBuckets.java b/core/java/com/android/internal/os/BinderLatencyBuckets.java
index d7d2d6a..5679bc7 100644
--- a/core/java/com/android/internal/os/BinderLatencyBuckets.java
+++ b/core/java/com/android/internal/os/BinderLatencyBuckets.java
@@ -26,6 +26,7 @@
* Generates the bucket thresholds (with a custom logarithmic scale) for a histogram to store
* latency samples in.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BinderLatencyBuckets {
private static final String TAG = "BinderLatencyBuckets";
private final int[] mBuckets;
diff --git a/core/java/com/android/internal/os/BinderfsStatsReader.java b/core/java/com/android/internal/os/BinderfsStatsReader.java
index 9cc4a35..66f91e1 100644
--- a/core/java/com/android/internal/os/BinderfsStatsReader.java
+++ b/core/java/com/android/internal/os/BinderfsStatsReader.java
@@ -43,6 +43,7 @@
* free async space 520192
* ...
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BinderfsStatsReader {
private final String mPath;
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
index 334cca3..ac92f86 100644
--- a/core/java/com/android/internal/os/CachedDeviceState.java
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -30,6 +30,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CachedDeviceState {
private volatile boolean mScreenInteractive;
private volatile boolean mCharging;
diff --git a/core/java/com/android/internal/os/Clock.java b/core/java/com/android/internal/os/Clock.java
index 45007c4..c2403d1 100644
--- a/core/java/com/android/internal/os/Clock.java
+++ b/core/java/com/android/internal/os/Clock.java
@@ -21,6 +21,7 @@
/**
* A wrapper for SystemClock, intended for mocking in unit tests.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class Clock {
/** Elapsed Realtime, see SystemClock.elapsedRealtime() */
public long elapsedRealtime() {
diff --git a/core/java/com/android/internal/os/CpuScalingPolicies.java b/core/java/com/android/internal/os/CpuScalingPolicies.java
index 6dbe8ab..f61cf97 100644
--- a/core/java/com/android/internal/os/CpuScalingPolicies.java
+++ b/core/java/com/android/internal/os/CpuScalingPolicies.java
@@ -27,6 +27,7 @@
* CPU scaling policies: the policy IDs and corresponding supported scaling for those
* policies.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CpuScalingPolicies {
private final SparseArray<int[]> mCpusByPolicy;
private final SparseArray<int[]> mFreqsByPolicy;
diff --git a/core/java/com/android/internal/os/CpuScalingPolicyReader.java b/core/java/com/android/internal/os/CpuScalingPolicyReader.java
index c96089a..0d272fd 100644
--- a/core/java/com/android/internal/os/CpuScalingPolicyReader.java
+++ b/core/java/com/android/internal/os/CpuScalingPolicyReader.java
@@ -40,6 +40,7 @@
* href="https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html
* #policy-interface-in-sysfs">Policy Interface in sysfs</a>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CpuScalingPolicyReader {
private static final String TAG = "CpuScalingPolicyReader";
private static final String CPUFREQ_DIR = "/sys/devices/system/cpu/cpufreq";
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 0843741..5b6d1b6 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -244,7 +244,8 @@
}
/** Set the UID predicate for {@link #getProcessCpuUsage} */
- void setUidPredicate(Predicate<Integer> uidPredicate) {
+ @VisibleForTesting
+ public void setUidPredicate(Predicate<Integer> uidPredicate) {
mUidPredicate = uidPredicate;
}
diff --git a/core/java/com/android/internal/os/LoggingPrintStream.java b/core/java/com/android/internal/os/LoggingPrintStream.java
index d27874c..4bf92bb 100644
--- a/core/java/com/android/internal/os/LoggingPrintStream.java
+++ b/core/java/com/android/internal/os/LoggingPrintStream.java
@@ -36,6 +36,7 @@
* {@hide}
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class LoggingPrintStream extends PrintStream {
private final StringBuilder builder = new StringBuilder();
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 0645eb7..bbcea8a 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -36,6 +36,7 @@
*
* @hide Only for use within the system server.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LooperStats implements Looper.Observer {
public static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
private static final int SESSION_POOL_SIZE = 50;
diff --git a/core/java/com/android/internal/os/MonotonicClock.java b/core/java/com/android/internal/os/MonotonicClock.java
index 661628a..c3bcfa6 100644
--- a/core/java/com/android/internal/os/MonotonicClock.java
+++ b/core/java/com/android/internal/os/MonotonicClock.java
@@ -40,6 +40,7 @@
* A clock that is similar to SystemClock#elapsedRealtime(), except that it is not reset
* on reboot, but keeps going.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MonotonicClock {
private static final String TAG = "MonotonicClock";
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 1a7efac..56263fb 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -41,6 +41,7 @@
* Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
* details.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PowerStats {
private static final String TAG = "PowerStats";
@@ -67,6 +68,7 @@
* This descriptor is used for storing PowerStats and can also be used by power models
* to adjust the algorithm in accordance with the stats available on the device.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class Descriptor {
public static final String XML_TAG_DESCRIPTOR = "descriptor";
private static final String XML_ATTR_ID = "id";
diff --git a/core/java/com/android/internal/os/ProcLocksReader.java b/core/java/com/android/internal/os/ProcLocksReader.java
index 9ddb8c7..6b85e08 100644
--- a/core/java/com/android/internal/os/ProcLocksReader.java
+++ b/core/java/com/android/internal/os/ProcLocksReader.java
@@ -34,6 +34,7 @@
* 3: POSIX ADVISORY READ 3888 fd:09:13992 128 128
* 4: POSIX ADVISORY READ 3888 fd:09:14230 1073741826 1073742335
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ProcLocksReader {
private final String mPath;
private ProcFileReader mReader = null;
diff --git a/core/java/com/android/internal/os/ProcStatsUtil.java b/core/java/com/android/internal/os/ProcStatsUtil.java
index 0002447f..b67190b 100644
--- a/core/java/com/android/internal/os/ProcStatsUtil.java
+++ b/core/java/com/android/internal/os/ProcStatsUtil.java
@@ -30,6 +30,7 @@
* Utility functions for reading {@code proc} files
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ProcStatsUtil {
private static final boolean DEBUG = false;
@@ -92,10 +93,24 @@
* seen, or at the end of the file
*/
@Nullable
+ @android.ravenwood.annotation.RavenwoodReplace
public static String readTerminatedProcFile(String path, byte terminator) {
// Permit disk reads here, as /proc isn't really "on disk" and should be fast.
// TODO: make BlockGuard ignore /proc/ and /sys/ files perhaps?
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ return readTerminatedProcFileInternal(path, terminator);
+ } finally {
+ StrictMode.setThreadPolicy(savedPolicy);
+ }
+ }
+
+ public static String readTerminatedProcFile$ravenwood(String path, byte terminator) {
+ // No StrictMode under Ravenwood
+ return readTerminatedProcFileInternal(path, terminator);
+ }
+
+ private static String readTerminatedProcFileInternal(String path, byte terminator) {
try (FileInputStream is = new FileInputStream(path)) {
ByteArrayOutputStream byteStream = null;
final byte[] buffer = new byte[READ_SIZE];
@@ -147,8 +162,6 @@
Slog.d(TAG, "Failed to open proc file", e);
}
return null;
- } finally {
- StrictMode.setThreadPolicy(savedPolicy);
}
}
}
diff --git a/core/java/com/android/internal/os/StoragedUidIoStatsReader.java b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
index 9b03469..2d485da 100644
--- a/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
+++ b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java
@@ -38,6 +38,7 @@
* This provides the number of bytes/chars read/written in foreground/background for each uid.
* The file contains a monotonically increasing count of bytes/chars for a single boot.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StoragedUidIoStatsReader {
private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName();
@@ -73,8 +74,21 @@
*
* @param callback The callback to invoke for each line of the proc file.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public void readAbsolute(Callback callback) {
final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try {
+ readAbsoluteInternal(callback);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ }
+ }
+
+ public void readAbsolute$ravenwood(Callback callback) {
+ readAbsoluteInternal(callback);
+ }
+
+ private void readAbsoluteInternal(Callback callback) {
File file = new File(sUidIoFile);
try (BufferedReader reader = Files.newBufferedReader(file.toPath())) {
String line;
@@ -106,8 +120,6 @@
}
} catch (IOException e) {
Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage());
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
}
}
}
diff --git a/core/jni/android_os_HidlSupport.cpp b/core/jni/android_os_HidlSupport.cpp
index e3602d8..3e51e93 100644
--- a/core/jni/android_os_HidlSupport.cpp
+++ b/core/jni/android_os_HidlSupport.cpp
@@ -15,6 +15,7 @@
*/
#include <hidl/HidlTransportSupport.h>
+#include <hidl/ServiceManagement.h>
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
@@ -24,8 +25,13 @@
return android::hardware::details::getPidIfSharable();
}
+static jboolean android_os_HidlSupport_isHidlSupported(JNIEnv*, jclass) {
+ return android::hardware::isHidlSupported();
+}
+
static const JNINativeMethod gHidlSupportMethods[] = {
- {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable},
+ {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable},
+ {"isHidlSupported", "()Z", (void*)android_os_HidlSupport_isHidlSupported},
};
const char* const kHidlSupportPathName = "android/os/HidlSupport";
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 3887dd7..9ca1849 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -99,6 +99,7 @@
optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_magnification_two_finger_triple_tap_enabled = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto qs_targets = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dd93586..c6a241f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4806,6 +4806,13 @@
<permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
android:protectionLevel="signature" />
+ <!-- @FlaggedApi("com.android.server.accessibility.motion_event_observing")
+ @hide
+ @TestApi
+ Allows an accessibility service to observe motion events without consuming them. -->
+ <permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
android:protectionLevel="signature" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 6706c91..1a3ec27 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -7,19 +7,35 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "FrameworksCoreTests-aidl",
+ srcs: [
+ "src/**/I*.aidl",
+ "aidl/**/I*.aidl",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "FrameworksCoreTests-helpers",
+ srcs: [
+ "DisabledTestApp/src/**/*.java",
+ "EnabledTestApp/src/**/*.java",
+ "BinderProxyCountingTestApp/src/**/*.java",
+ "BinderProxyCountingTestService/src/**/*.java",
+ "BinderDeathRecipientHelperApp/src/**/*.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
android_test {
name: "FrameworksCoreTests",
srcs: [
"src/**/*.java",
"src/**/*.kt",
- "src/**/I*.aidl",
- "DisabledTestApp/src/**/*.java",
- "EnabledTestApp/src/**/*.java",
- "BinderProxyCountingTestApp/src/**/*.java",
- "BinderProxyCountingTestService/src/**/*.java",
- "BinderDeathRecipientHelperApp/src/**/*.java",
- "aidl/**/I*.aidl",
+ ":FrameworksCoreTests-aidl",
+ ":FrameworksCoreTests-helpers",
":FrameworksCoreTestDoubles-sources",
],
@@ -177,17 +193,29 @@
"androidx.annotation_annotation",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "flag-junit",
"mockito_ravenwood",
"platform-test-annotations",
"flag-junit",
+ "testng",
],
srcs: [
- "src/android/os/BuildTest.java",
- "src/android/os/FileUtilsTest.java",
+ "src/android/os/**/*.java",
+ "src/com/android/internal/os/**/*.java",
"src/android/util/**/*.java",
"src/com/android/internal/os/LongArrayMultiStateCounterTest.java",
"src/com/android/internal/util/**/*.java",
- "testdoubles/src/com/android/internal/util/**/*.java",
+ "src/com/android/internal/power/EnergyConsumerStatsTest.java",
+ ":FrameworksCoreTests{.aapt.srcjar}",
+ ":FrameworksCoreTests-aidl",
+ ":FrameworksCoreTests-helpers",
+ ":FrameworksCoreTestDoubles-sources",
],
+ aidl: {
+ generate_get_transaction_name: true,
+ local_include_dirs: ["aidl"],
+ },
auto_gen_config: true,
}
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 3768063..cd6abdd 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -20,9 +20,13 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
/**
@@ -35,7 +39,10 @@
* atest FrameworksCoreTests:PropertyInvalidatedCacheTests
*/
@SmallTest
+@IgnoreUnderRavenwood(blockedBy = PropertyInvalidatedCache.class)
public class PropertyInvalidatedCacheTests {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
// Configuration for creating caches
private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST;
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index d0c3470..006828f 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -16,23 +16,36 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
import com.google.android.collect.Lists;
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
import java.util.List;
-public class AidlTest extends TestCase {
+@IgnoreUnderRavenwood(blockedBy = Parcel.class)
+public class AidlTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private IAidlTest mRemote;
private AidlObject mLocal;
private NonAutoGeneratedObject mNonAutoGenerated;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
mLocal = new AidlObject();
mRemote = IAidlTest.Stub.asInterface(mLocal);
mNonAutoGenerated = new NonAutoGeneratedObject("NonAutoGeneratedObject");
@@ -212,12 +225,14 @@
}
}
+ @Test
@SmallTest
public void testInt() throws Exception {
int result = mRemote.intMethod(42);
assertEquals(42, result);
}
+ @Test
@SmallTest
public void testParcelableIn() throws Exception {
TestParcelable arg = new TestParcelable(43, "hi");
@@ -228,6 +243,7 @@
assertEquals(44, result.mAnInt);
}
+ @Test
@SmallTest
public void testParcelableOut() throws Exception {
TestParcelable arg = new TestParcelable(43, "hi");
@@ -236,6 +252,7 @@
assertEquals(44, arg.mAnInt);
}
+ @Test
@SmallTest
public void testParcelableInOut() throws Exception {
TestParcelable arg = new TestParcelable(43, "hi");
@@ -244,6 +261,7 @@
assertEquals(44, arg.mAnInt);
}
+ @Test
@SmallTest
public void testListParcelableLonger() throws Exception {
List<TestParcelable> list = Lists.newArrayList();
@@ -268,6 +286,7 @@
assertNotSame(list.get(1), list.get(2));
}
+ @Test
@SmallTest
public void testListParcelableShorter() throws Exception {
List<TestParcelable> list = Lists.newArrayList();
@@ -290,6 +309,7 @@
assertNotSame(list.get(0), list.get(1));
}
+ @Test
@SmallTest
public void testArrays() throws Exception {
// boolean
@@ -363,14 +383,14 @@
float[] fr = mRemote.floatArray(f0, f1, f2);
assertEquals(1, fr.length);
- assertEquals(90.1f, fr[0]);
+ assertEquals(90.1f, fr[0], 0.0f);
- assertEquals(90.1f, f1[0]);
+ assertEquals(90.1f, f1[0], 0.0f);
assertEquals(0f, f1[1], 0.0f);
- assertEquals(90.1f, f2[0]);
- assertEquals(90.5f, f2[1]);
- assertEquals(90.6f, f2[2]);
+ assertEquals(90.1f, f2[0], 0.0f);
+ assertEquals(90.5f, f2[1], 0.0f);
+ assertEquals(90.6f, f2[2], 0.0f);
// double
double[] d0 = new double[]{100.1};
@@ -379,14 +399,14 @@
double[] dr = mRemote.doubleArray(d0, d1, d2);
assertEquals(1, dr.length);
- assertEquals(100.1, dr[0]);
+ assertEquals(100.1, dr[0], 0.0);
- assertEquals(100.1, d1[0]);
+ assertEquals(100.1, d1[0], 0.0);
assertEquals(0, d1[1], 0.0);
- assertEquals(100.1, d2[0]);
- assertEquals(100.5, d2[1]);
- assertEquals(100.6, d2[2]);
+ assertEquals(100.1, d2[0], 0.0);
+ assertEquals(100.5, d2[1], 0.0);
+ assertEquals(100.6, d2[2], 0.0);
// String
String[] s0 = new String[]{"s0[0]"};
@@ -405,6 +425,7 @@
assertEquals("s2[2]", s2[2]);
}
+ @Test
@SmallTest
public void testVoidSecurityException() throws Exception {
boolean good = false;
@@ -416,6 +437,7 @@
assertEquals(good, true);
}
+ @Test
@SmallTest
public void testIntSecurityException() throws Exception {
boolean good = false;
@@ -427,6 +449,7 @@
assertEquals(good, true);
}
+ @Test
@SmallTest
public void testGetTransactionNameAutoGenerated() throws Exception {
assertEquals(15, mLocal.getMaxTransactionId());
@@ -446,6 +469,7 @@
mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_parcelableIn));
}
+ @Test
@SmallTest
public void testGetTransactionNameNonAutoGenerated() throws Exception {
assertEquals(0, mNonAutoGenerated.getMaxTransactionId());
diff --git a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
index 2cce43f..eff52f0 100644
--- a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
+++ b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
@@ -25,6 +25,8 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -36,6 +38,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +55,7 @@
* Tests functionality of {@link android.os.IBinder.DeathRecipient} callbacks.
*/
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
public class BinderDeathRecipientTest {
private static final String TAG = BinderDeathRecipientTest.class.getSimpleName();
private static final String TEST_PACKAGE_NAME_1 =
@@ -59,6 +63,9 @@
private static final String TEST_PACKAGE_NAME_2 =
"com.android.frameworks.coretests.bdr_helper_app2";
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Context mContext;
private Handler mHandler;
private ActivityManager mActivityManager;
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
index 2089c6c..bcd9521 100644
--- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -35,7 +37,8 @@
import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
-import org.junit.BeforeClass;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,6 +74,7 @@
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
public class BinderProxyCountingTest {
private static final String TAG = BinderProxyCountingTest.class.getSimpleName();
@@ -107,11 +111,14 @@
};
private static int sTestPkgUid;
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
/**
* Setup any common data for the upcoming tests.
*/
- @BeforeClass
- public static void setUpOnce() throws Exception {
+ @Before
+ public void setUp() throws Exception {
sContext = InstrumentationRegistry.getContext();
sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_APP_PKG, 0);
((ActivityManager) sContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(sTestPkgUid,
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index 3567d17..a903ed9 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -16,19 +16,34 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.test.AndroidTestCase;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-public class BinderProxyTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = PowerManager.class)
+public class BinderProxyTest {
private static class CountingListener implements Binder.ProxyTransactListener {
int mStartedCount;
int mEndedCount;
@@ -43,17 +58,22 @@
}
};
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ private Context mContext;
private PowerManager mPowerManager;
/**
* Setup any common data for the upcoming tests.
*/
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
+ @Test
@MediumTest
public void testNoListener() throws Exception {
CountingListener listener = new CountingListener();
@@ -66,6 +86,7 @@
assertEquals(0, listener.mEndedCount);
}
+ @Test
@MediumTest
public void testListener() throws Exception {
CountingListener listener = new CountingListener();
@@ -77,6 +98,7 @@
assertEquals(1, listener.mEndedCount);
}
+ @Test
@MediumTest
public void testSessionPropagated() throws Exception {
Binder.setProxyTransactListener(new Binder.ProxyTransactListener() {
@@ -95,6 +117,7 @@
private IBinder mRemoteBinder = null;
+ @Test
@MediumTest
public void testGetExtension() throws Exception {
final CountDownLatch bindLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index 02f8790..6c8b69f 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -16,21 +16,35 @@
package android.os;
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
import static org.testng.Assert.assertThrows;
-public class BinderTest extends TestCase {
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@IgnoreUnderRavenwood(blockedBy = WorkSource.class)
+public class BinderTest {
private static final int UID = 100;
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
@SmallTest
public void testSetWorkSource() throws Exception {
Binder.setCallingWorkSourceUid(UID);
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
+ @Test
@SmallTest
public void testClearWorkSource() throws Exception {
Binder.setCallingWorkSourceUid(UID);
@@ -38,6 +52,7 @@
assertEquals(-1, Binder.getCallingWorkSourceUid());
}
+ @Test
@SmallTest
public void testRestoreWorkSource() throws Exception {
Binder.setCallingWorkSourceUid(UID);
@@ -46,11 +61,13 @@
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
+ @Test
@SmallTest
public void testGetCallingUidOrThrow_throws() throws Exception {
assertThrows(IllegalStateException.class, () -> Binder.getCallingUidOrThrow());
}
+ @Test
@SmallTest
public void testGetExtension() throws Exception {
Binder binder = new Binder();
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
index 48c9df6..4172bff 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
@@ -16,21 +16,42 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.test.AndroidTestCase;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.IOException;
/**
* Test whether Binder calls inherit thread priorities correctly.
*/
-public class BinderThreadPriorityTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+public class BinderThreadPriorityTest {
private static final String TAG = "BinderThreadPriorityTest";
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ private Context mContext;
private IBinderThreadPriorityService mService;
private int mSavedPriority;
@@ -55,12 +76,11 @@
private static void fail() { throw new RuntimeException("unimplemented"); }
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- getContext().bindService(
- new Intent(getContext(), BinderThreadPriorityService.class),
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mContext.bindService(
+ new Intent(mContext, BinderThreadPriorityService.class),
mConnection, Context.BIND_AUTO_CREATE);
synchronized (this) {
@@ -80,8 +100,8 @@
Log.i(TAG, "Saved priority: " + mSavedPriority);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
// HACK -- see bug 2665914 -- setThreadPriority() doesn't always set the
// scheduler group reliably unless we start out with background priority.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -89,8 +109,7 @@
assertEquals(mSavedPriority, Process.getThreadPriority(Process.myTid()));
assertEquals(expectedSchedulerGroup(mSavedPriority), getSchedulerGroup());
- getContext().unbindService(mConnection);
- super.tearDown();
+ mContext.unbindService(mConnection);
}
public static String getSchedulerGroup() {
@@ -111,6 +130,7 @@
return "/";
}
+ @Test
public void testPassPriorityToService() throws Exception {
for (int prio = 19; prio >= -20; prio--) {
Process.setThreadPriority(prio);
@@ -125,6 +145,7 @@
}
}
+ @Test
public void testCallBackFromServiceWithPriority() throws Exception {
for (int prio = -20; prio <= 19; prio++) {
final int expected = prio;
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index b14c88f..552066c 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -19,11 +19,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -33,6 +36,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,12 +46,16 @@
@LargeTest
@Presubmit
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
public class BinderWorkSourceTest {
private static Context sContext;
private static final int UID = 100;
private static final int SECOND_UID = 200;
private static final int UID_NONE = ThreadLocalWorkSource.UID_NONE;
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private IBinderWorkSourceService mService;
private IBinderWorkSourceNestedService mNestedService;
@@ -71,13 +79,9 @@
}
};
- @BeforeClass
- public static void setUpOnce() throws Exception {
- sContext = InstrumentationRegistry.getContext();
- }
-
@Before
public void setUp() throws Exception {
+ sContext = InstrumentationRegistry.getContext();
sContext.bindService(
new Intent(sContext, BinderWorkSourceService.class),
mConnection, Context.BIND_AUTO_CREATE);
diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java
index b4c47af9..7829457 100644
--- a/core/tests/coretests/src/android/os/BroadcasterTest.java
+++ b/core/tests/coretests/src/android/os/BroadcasterTest.java
@@ -16,16 +16,26 @@
package android.os;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class BroadcasterTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class BroadcasterTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final int MESSAGE_A = 23234;
private static final int MESSAGE_B = 3;
private static final int MESSAGE_C = 14;
private static final int MESSAGE_D = 95;
+ @Test
@MediumTest
public void test1() throws Exception {
/*
@@ -103,6 +113,7 @@
}
}
+ @Test
@MediumTest
public void test2() throws Exception {
/*
@@ -112,6 +123,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void test3() throws Exception {
/*
@@ -121,6 +133,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void test4() throws Exception {
/*
@@ -156,6 +169,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void test5() throws Exception {
/*
@@ -191,6 +205,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void test6() throws Exception {
/*
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index a3bda8b..8c231de 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -23,13 +23,16 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,6 +48,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class BundleTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Log.TerribleFailureHandler mWtfHandler;
@After
@@ -115,6 +121,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class)
public void testCreateFromParcel() throws Exception {
boolean withFd;
Parcel p;
@@ -190,6 +197,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_bothParcelled_same() {
Bundle bundle1 = new Bundle();
bundle1.putString("StringKey", "S");
@@ -207,6 +215,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_bothParcelled_different() {
Bundle bundle1 = new Bundle();
bundle1.putString("StringKey", "S");
@@ -238,6 +247,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_lazyValues() {
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
@@ -271,6 +281,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() {
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
@@ -295,6 +306,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void kindofEquals_lazyValuesAndDifferentClassLoaders_returnsFalse() {
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
@@ -350,6 +362,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void readWriteLengthMismatch_logsWtf() throws Exception {
mWtfHandler = Log.setWtfHandler((tag, e, system) -> {
throw new RuntimeException(e);
@@ -364,6 +377,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void getParcelable_whenThrowingAndNotDefusing_throws() throws Exception {
Bundle.setShouldDefuse(false);
Bundle bundle = new Bundle();
@@ -376,6 +390,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void getParcelable_whenThrowingAndDefusing_returnsNull() throws Exception {
Bundle.setShouldDefuse(true);
Bundle bundle = new Bundle();
@@ -391,6 +406,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void getParcelable_whenThrowingAndDefusing_leavesElement() throws Exception {
Bundle.setShouldDefuse(true);
Bundle bundle = new Bundle();
diff --git a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
index 42c97f3..c2cea0a 100644
--- a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
+++ b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
@@ -22,6 +22,8 @@
import android.content.Context;
import android.os.CancellationSignalBeamer.Receiver;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.PollingCheck;
import android.util.PollingCheck.PollingCheckCondition;
@@ -29,6 +31,8 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,11 +44,20 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@IgnoreUnderRavenwood(blockedBy = CancellationSignalBeamer.class)
public class CancellationSignalBeamerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
- private CancellationSignal mSenderSignal = new CancellationSignal();
+ private CancellationSignal mSenderSignal;
private CancellationSignal mReceivedSignal;
- private Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mSenderSignal = new CancellationSignal();
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ }
@Test
public void testBeam_null() {
@@ -208,17 +221,22 @@
mReceivedSignal = mReceiver.unbeam(cancellationSignalToken);
}
- private final Sender mSender = new Sender() {
- @Override
- public void onCancel(IBinder token) {
- mReceiver.cancel(token);
- }
+ private Sender mSender;
+ private Receiver mReceiver;
- @Override
- public void onForget(IBinder token) {
- mReceiver.forget(token);
- }
- };
+ @Before
+ public void setUpSenderReceiver() {
+ mSender = new Sender() {
+ @Override
+ public void onCancel(IBinder token) {
+ mReceiver.cancel(token);
+ }
- private final Receiver mReceiver = new Receiver(false);
+ @Override
+ public void onForget(IBinder token) {
+ mReceiver.forget(token);
+ }
+ };
+ mReceiver = new Receiver(false);
+ }
}
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
index ef38cde..1aa263f 100644
--- a/core/tests/coretests/src/android/os/EnvironmentTest.java
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -28,12 +28,15 @@
import android.content.Context;
import android.os.storage.StorageManager;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,7 +46,11 @@
import java.util.function.BiFunction;
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = Environment.class)
public class EnvironmentTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File dir;
private static Context getContext() {
diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java
index 708bfa6..726670b 100644
--- a/core/tests/coretests/src/android/os/FileBridgeTest.java
+++ b/core/tests/coretests/src/android/os/FileBridgeTest.java
@@ -19,12 +19,25 @@
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.os.FileBridge.FileBridgeOutputStream;
-import android.test.AndroidTestCase;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.test.MoreAsserts;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import libcore.io.Streams;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -32,18 +45,20 @@
import java.nio.charset.StandardCharsets;
import java.util.Random;
-public class FileBridgeTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class)
+public class FileBridgeTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private File file;
private ParcelFileDescriptor outputFile;
private FileBridge bridge;
private FileBridgeOutputStream client;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- file = getContext().getFileStreamPath("meow.dat");
+ @Before
+ public void setUp() throws Exception {
+ file = File.createTempFile("meow", "dat");
file.delete();
outputFile = ParcelFileDescriptor.open(file, MODE_CREATE | MODE_READ_WRITE);
@@ -54,8 +69,8 @@
client = new FileBridgeOutputStream(bridge.getClientSocket());
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
outputFile.close();
file.delete();
}
@@ -76,17 +91,20 @@
MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file)));
}
+ @Test
public void testNoWriteNoSync() throws Exception {
assertOpen();
closeAndAssertClosed();
}
+ @Test
public void testNoWriteSync() throws Exception {
assertOpen();
client.flush();
closeAndAssertClosed();
}
+ @Test
public void testWriteNoSync() throws Exception {
assertOpen();
client.write("meow".getBytes(StandardCharsets.UTF_8));
@@ -94,6 +112,7 @@
assertContents("meow".getBytes(StandardCharsets.UTF_8));
}
+ @Test
public void testWriteSync() throws Exception {
assertOpen();
client.write("cake".getBytes(StandardCharsets.UTF_8));
@@ -102,6 +121,7 @@
assertContents("cake".getBytes(StandardCharsets.UTF_8));
}
+ @Test
public void testWriteSyncWrite() throws Exception {
assertOpen();
client.write("meow".getBytes(StandardCharsets.UTF_8));
@@ -111,6 +131,7 @@
assertContents("meowcake".getBytes(StandardCharsets.UTF_8));
}
+ @Test
public void testEmptyWrite() throws Exception {
assertOpen();
client.write(new byte[0]);
@@ -118,6 +139,7 @@
assertContents(new byte[0]);
}
+ @Test
public void testWriteAfterClose() throws Exception {
assertOpen();
client.write("meow".getBytes(StandardCharsets.UTF_8));
@@ -130,6 +152,7 @@
assertContents("meow".getBytes(StandardCharsets.UTF_8));
}
+ @Test
public void testRandomWrite() throws Exception {
final Random r = new Random();
final ByteArrayOutputStream result = new ByteArrayOutputStream();
@@ -146,6 +169,7 @@
assertContents(result.toByteArray());
}
+ @Test
public void testGiantWrite() throws Exception {
final byte[] test = new byte[263401];
new Random().nextBytes(test);
diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java
index ece7645..3cd8045 100644
--- a/core/tests/coretests/src/android/os/FileObserverTest.java
+++ b/core/tests/coretests/src/android/os/FileObserverTest.java
@@ -16,21 +16,37 @@
package android.os;
-import android.test.AndroidTestCase;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.FileOutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-public class FileObserverTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = FileObserver.class)
+public class FileObserverTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Observer mObserver;
private File mTestFile;
@@ -57,18 +73,19 @@
}
}
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
mTestFile = File.createTempFile(".file_observer_test", ".txt");
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
if (mTestFile != null && mTestFile.exists()) {
mTestFile.delete();
}
}
+ @Test
@MediumTest
public void testRun() throws Exception {
// make file changes and wait for them
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
index 93cfc40..0bac1c7 100644
--- a/core/tests/coretests/src/android/os/HandlerThreadTest.java
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -16,18 +16,35 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class HandlerThreadTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class HandlerThreadTest {
private static final int TEST_WHAT = 1;
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private boolean mGotMessage = false;
private int mGotMessageWhat = -1;
private volatile boolean mDidSetup = false;
private volatile int mLooperTid = -1;
-
+
+ @Test
@MediumTest
public void testHandlerThread() throws Exception {
HandlerThread th1 = new HandlerThread("HandlerThreadTest") {
diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java
index d8886c9..8644663 100644
--- a/core/tests/coretests/src/android/os/IdleHandlerTest.java
+++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java
@@ -17,12 +17,19 @@
package android.os;
import android.os.MessageQueue.IdleHandler;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class IdleHandlerTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class IdleHandlerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private static class BaseTestHandler extends TestHandlerThread {
Handler mHandler;
@@ -54,6 +61,7 @@
}
}
+ @Test
@MediumTest
public void testOneShotFirst() throws Exception {
TestHandlerThread tester = new BaseTestHandler() {
@@ -88,6 +96,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void testOneShotLater() throws Exception {
TestHandlerThread tester = new BaseTestHandler() {
@@ -125,6 +134,7 @@
}
+ @Test
@MediumTest
public void testRepeatedFirst() throws Exception {
TestHandlerThread tester = new BaseTestHandler() {
@@ -159,6 +169,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void testRepeatedLater() throws Exception {
TestHandlerThread tester = new BaseTestHandler() {
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 34712ce..b03fd64 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -17,12 +17,14 @@
package android.os;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
/**
@@ -35,7 +37,10 @@
* atest FrameworksCoreTests:IpcDataCacheTest
*/
@SmallTest
+@IgnoreUnderRavenwood(blockedBy = IpcDataCache.class)
public class IpcDataCacheTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
// Configuration for creating caches
private static final String MODULE = IpcDataCache.MODULE_TEST;
diff --git a/core/tests/coretests/src/android/os/LocaleListTest.java b/core/tests/coretests/src/android/os/LocaleListTest.java
index 88fc826..0025e3a 100644
--- a/core/tests/coretests/src/android/os/LocaleListTest.java
+++ b/core/tests/coretests/src/android/os/LocaleListTest.java
@@ -16,13 +16,29 @@
package android.os;
-import androidx.test.filters.SmallTest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
-import junit.framework.TestCase;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Locale;
-public class LocaleListTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = LocaleList.class)
+public class LocaleListTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
@SmallTest
public void testConstructor() throws Exception {
LocaleList ll;
@@ -51,6 +67,7 @@
assertEquals("fr,de", ll.toLanguageTags());
}
+ @Test
@SmallTest
public void testConstructor_nullThrows() throws Exception {
try {
@@ -61,6 +78,7 @@
}
}
+ @Test
@SmallTest
public void testGetDefault_localeSetDefaultCalledButNoChangeNecessary() throws Exception {
final Locale originalLocale = Locale.getDefault();
@@ -82,6 +100,7 @@
LocaleList.setDefault(originalLocaleList, originalLocaleIndex);
}
+ @Test
@SmallTest
public void testIntersection() {
LocaleList localesWithN = new LocaleList(
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 05c2995..a695424 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -16,11 +16,21 @@
package android.os;
-import android.test.AndroidTestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SmallTest;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -29,7 +39,11 @@
import java.util.Arrays;
import java.util.List;
-public class MemoryFileTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = MemoryFile.class)
+public class MemoryFileTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private void compareBuffers(byte[] buffer1, byte[] buffer2, int length) throws Exception {
for (int i = 0; i < length; i++) {
@@ -44,6 +58,8 @@
*/
// Flaky test - temporarily suppress from large suite for now
// @LargeTest
+ @Test
+ @Ignore("Flaky test")
public void testPurge() throws Exception {
List<MemoryFile> files = new ArrayList<MemoryFile>();
try {
@@ -70,6 +86,7 @@
}
}
+ @Test
@SmallTest
public void testRun() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
@@ -102,6 +119,7 @@
}
// http://code.google.com/p/android/issues/detail?id=11415
+ @Test
public void testOutputStreamAdvances() throws IOException {
MemoryFile file = new MemoryFile("MemoryFileTest", 10);
@@ -142,24 +160,28 @@
}
}
+ @Test
@SmallTest
public void testReadNegativeOffset() throws Exception {
readIndexOutOfBoundsException(-1, 5,
"read() with negative offset should throw IndexOutOfBoundsException");
}
+ @Test
@SmallTest
public void testReadNegativeCount() throws Exception {
readIndexOutOfBoundsException(5, -1,
"read() with negative length should throw IndexOutOfBoundsException");
}
+ @Test
@SmallTest
public void testReadOffsetOverflow() throws Exception {
readIndexOutOfBoundsException(testString.length + 10, 5,
"read() with offset outside buffer should throw IndexOutOfBoundsException");
}
+ @Test
@SmallTest
public void testReadOffsetCountOverflow() throws Exception {
readIndexOutOfBoundsException(testString.length, 11,
@@ -167,6 +189,7 @@
}
// Test behavior of read() at end of file
+ @Test
@SmallTest
public void testReadEOF() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
@@ -189,6 +212,7 @@
}
// Tests that close() is idempotent
+ @Test
@SmallTest
public void testCloseClose() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
@@ -199,6 +223,7 @@
}
// Tests that we can't read from a closed memory file
+ @Test
@SmallTest
public void testCloseRead() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
@@ -214,6 +239,7 @@
}
// Tests that we can't write to a closed memory file
+ @Test
@SmallTest
public void testCloseWrite() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
@@ -229,6 +255,7 @@
}
// Tests that we can't call allowPurging() after close()
+ @Test
@SmallTest
public void testCloseAllowPurging() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
@@ -245,6 +272,7 @@
}
// Tests that we don't leak file descriptors or mmap areas
+ @Test
@LargeTest
public void testCloseLeak() throws Exception {
// open enough memory files that we should run out of
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 2c5588e..8cd6773 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,13 +16,21 @@
package android.os;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.MediumTest;
import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@Suppress // Failing.
-public class MessageQueueTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class MessageQueueTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private static class BaseTestHandler extends TestHandlerThread {
Handler mHandler;
@@ -61,6 +69,7 @@
}
}
+ @Test
@MediumTest
public void testMessageOrder() throws Exception {
TestHandlerThread tester = new BaseTestHandler() {
@@ -80,6 +89,7 @@
tester.doTest(1000);
}
+ @Test
@MediumTest
public void testAtFrontOfQueue() throws Exception {
TestHandlerThread tester = new BaseTestHandler() {
@@ -141,6 +151,7 @@
}
}
+ @Test
@MediumTest
public void testFieldIntegrity() throws Exception {
@@ -157,7 +168,7 @@
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
- msg.flags = -1;
+ msg.flags = Message.FLAGS_TO_CLEAR_ON_COPY_FROM;
msg.what = 1;
msg.arg1 = 456;
msg.arg2 = 789;
diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java
index 9143ff1..eb6263f 100644
--- a/core/tests/coretests/src/android/os/MessengerTest.java
+++ b/core/tests/coretests/src/android/os/MessengerTest.java
@@ -16,15 +16,31 @@
package android.os;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.test.AndroidTestCase;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
-public class MessengerTest extends AndroidTestCase {
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+public class MessengerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ private Context mContext;
private Messenger mServiceMessenger;
private ServiceConnection mConnection = new ServiceConnection() {
@@ -86,10 +102,10 @@
}
};
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getContext().bindService(new Intent(mContext, MessengerService.class),
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mContext.bindService(new Intent(mContext, MessengerService.class),
mConnection, Context.BIND_AUTO_CREATE);
synchronized (this) {
while (mServiceMessenger == null) {
@@ -101,15 +117,14 @@
}
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- getContext().unbindService(mConnection);
+ @After
+ public void tearDown() throws Exception {
+ mContext.unbindService(mConnection);
}
+ @Test
@MediumTest
public void testSend() {
(new TestThread()).doTest(1000);
-
}
}
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
deleted file mode 100644
index 08fb945..0000000
--- a/core/tests/coretests/src/android/os/OsTests.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2006 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 junit.framework.TestSuite;
-
-public class OsTests {
- public static TestSuite suite() {
- TestSuite suite = new TestSuite(OsTests.class.getName());
-
- suite.addTestSuite(AidlTest.class);
- suite.addTestSuite(BroadcasterTest.class);
- suite.addTestSuite(FileObserverTest.class);
- suite.addTestSuite(IdleHandlerTest.class);
- suite.addTestSuite(MessageQueueTest.class);
- suite.addTestSuite(MessengerTest.class);
- suite.addTestSuite(PatternMatcherTest.class);
-
- return suite;
- }
-}
diff --git a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
index b4e180c..ffeab29 100644
--- a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
+++ b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
@@ -20,11 +20,14 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +39,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class ParcelNullabilityTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void nullByteArray() {
Parcel p = Parcel.obtain();
@@ -61,6 +67,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void nullCharSequence() {
Parcel p = Parcel.obtain();
p.writeCharSequence(null);
@@ -69,6 +76,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void nullStrongBinder() {
Parcel p = Parcel.obtain();
p.writeStrongBinder(null);
@@ -77,6 +85,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void nullStringInterface() {
Parcel p = Parcel.obtain();
p.writeStrongInterface(null);
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 4b993fa..5bbd221 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -21,22 +21,29 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ParcelTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final int WORK_SOURCE_1 = 1000;
private static final int WORK_SOURCE_2 = 1002;
private static final String INTERFACE_TOKEN_1 = "IBinder interface token";
private static final String INTERFACE_TOKEN_2 = "Another IBinder interface token";
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testIsForRpc() {
Parcel p = Parcel.obtain();
assertEquals(false, p.isForRpc());
@@ -44,6 +51,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCallingWorkSourceUidAfterWrite() {
Parcel p = Parcel.obtain();
// Method does not throw if replaceCallingWorkSourceUid is called before requests headers
@@ -64,6 +72,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCallingWorkSourceUidAfterEnforce() {
Parcel p = Parcel.obtain();
p.writeInterfaceToken(INTERFACE_TOKEN_1);
@@ -81,6 +90,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testParcelWithMultipleHeaders() {
Parcel p = Parcel.obtain();
Binder.setCallingWorkSourceUid(WORK_SOURCE_1);
@@ -122,6 +132,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenSameData() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -138,6 +149,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenSameDataWithBinder() {
Binder binder = new Binder();
Parcel pA = Parcel.obtain();
@@ -157,6 +169,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenDifferentData() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -173,6 +186,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenLimitOutOfBounds_throws() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -199,6 +213,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenLengthZero() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -217,6 +232,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenNegativeLength_throws() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -232,6 +248,7 @@
}
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testCompareDataInRange_whenNegativeOffset_throws() {
Parcel pA = Parcel.obtain();
int iA = pA.dataPosition();
@@ -297,6 +314,7 @@
* and 1M length for complex objects are allowed.
*/
@Test
+ @IgnoreUnderRavenwood(blockedBy = Parcel.class)
public void testAllocations_whenWithinLimit() {
Binder.setIsDirectlyHandlingTransactionOverride(true);
Parcel p = Parcel.obtain();
diff --git a/core/tests/coretests/src/android/os/PatternMatcherTest.java b/core/tests/coretests/src/android/os/PatternMatcherTest.java
index 82350cd..a5e036d 100644
--- a/core/tests/coretests/src/android/os/PatternMatcherTest.java
+++ b/core/tests/coretests/src/android/os/PatternMatcherTest.java
@@ -16,9 +16,10 @@
package android.os;
-import androidx.test.filters.SmallTest;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import junit.framework.TestCase;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,7 +27,7 @@
@RunWith(JUnit4.class)
@SmallTest
-public class PatternMatcherTest extends TestCase{
+public class PatternMatcherTest {
@Test
public void testAdvancedPatternMatchesAnyToken() {
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
index 38ad90f..46f1706 100644
--- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -16,33 +16,48 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
import android.os.PerformanceCollector.PerformanceResultsWriter;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Random;
-public class PerformanceCollectorTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = PerformanceCollector.class)
+public class PerformanceCollectorTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private PerformanceCollector mPerfCollector;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
mPerfCollector = new PerformanceCollector();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
mPerfCollector = null;
}
+ @Test
@SmallTest
public void testBeginSnapshotNoWriter() throws Exception {
mPerfCollector.beginSnapshot("testBeginSnapshotNoWriter");
@@ -54,6 +69,7 @@
assertEquals(2, snapshot.size());
}
+ @Test
@MediumTest
public void testEndSnapshotNoWriter() throws Exception {
mPerfCollector.beginSnapshot("testEndSnapshotNoWriter");
@@ -63,6 +79,7 @@
verifySnapshotBundle(snapshot);
}
+ @Test
@SmallTest
public void testStartTimingNoWriter() throws Exception {
mPerfCollector.startTiming("testStartTimingNoWriter");
@@ -74,6 +91,7 @@
verifyTimingBundle(measurement, new ArrayList<String>());
}
+ @Test
@SmallTest
public void testAddIterationNoWriter() throws Exception {
mPerfCollector.startTiming("testAddIterationNoWriter");
@@ -83,6 +101,7 @@
verifyIterationBundle(iteration, "timing1");
}
+ @Test
@SmallTest
public void testStopTimingNoWriter() throws Exception {
mPerfCollector.startTiming("testStopTimingNoWriter");
@@ -100,6 +119,7 @@
verifyTimingBundle(timing, labels);
}
+ @Test
@SmallTest
public void testBeginSnapshot() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -114,6 +134,7 @@
assertEquals(2, snapshot.size());
}
+ @Test
@MediumTest
public void testEndSnapshot() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -127,6 +148,7 @@
verifySnapshotBundle(snapshot1);
}
+ @Test
@SmallTest
public void testStartTiming() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -141,6 +163,7 @@
verifyTimingBundle(measurement, new ArrayList<String>());
}
+ @Test
@SmallTest
public void testAddIteration() throws Exception {
mPerfCollector.startTiming("testAddIteration");
@@ -150,6 +173,7 @@
verifyIterationBundle(iteration, "timing5");
}
+ @Test
@SmallTest
public void testStopTiming() throws Exception {
mPerfCollector.startTiming("testStopTiming");
@@ -167,6 +191,7 @@
verifyTimingBundle(timing, labels);
}
+ @Test
@SmallTest
public void testAddMeasurementLong() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -188,6 +213,7 @@
assertEquals(-19354, results.getLong("testAddMeasurementLongNeg"));
}
+ @Test
@SmallTest
public void testAddMeasurementFloat() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -202,13 +228,14 @@
Bundle results = writer.timingResults;
assertEquals(4, results.size());
assertTrue(results.containsKey("testAddMeasurementFloatZero"));
- assertEquals(0.0f, results.getFloat("testAddMeasurementFloatZero"));
+ assertEquals(0.0f, results.getFloat("testAddMeasurementFloatZero"), 0.0f);
assertTrue(results.containsKey("testAddMeasurementFloatPos"));
- assertEquals(348573.345f, results.getFloat("testAddMeasurementFloatPos"));
+ assertEquals(348573.345f, results.getFloat("testAddMeasurementFloatPos"), 0.0f);
assertTrue(results.containsKey("testAddMeasurementFloatNeg"));
- assertEquals(-19354.093f, results.getFloat("testAddMeasurementFloatNeg"));
+ assertEquals(-19354.093f, results.getFloat("testAddMeasurementFloatNeg"), 0.0f);
}
+ @Test
@SmallTest
public void testAddMeasurementString() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -230,6 +257,7 @@
assertEquals("Hello World", results.getString("testAddMeasurementStringNonEmpty"));
}
+ @Test
@MediumTest
public void testSimpleSequence() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -262,6 +290,7 @@
verifyTimingBundle(timing, labels);
}
+ @Test
@MediumTest
public void testLongSequence() throws Exception {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
@@ -348,6 +377,7 @@
* Verify that snapshotting and timing do not interfere w/ each other,
* by staggering calls to snapshot and timing functions.
*/
+ @Test
@MediumTest
public void testOutOfOrderSequence() {
MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 9b4dec4..12a2844 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -24,18 +24,25 @@
import static org.junit.Assume.assumeNotNull;
import android.os.PerformanceHintManager.Session;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = PerformanceHintManager.class)
public class PerformanceHintManagerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final long RATE_1000 = 1000L;
private static final long TARGET_166 = 166L;
private static final long DEFAULT_TARGET_NS = 16666666L;
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 5d213ca..cb281ff 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -17,7 +17,9 @@
package android.os;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -26,28 +28,34 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.os.Flags;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.AndroidTestCase;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiDevice;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-public class PowerManagerTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = PowerManager.class)
+public class PowerManagerTest {
private static final String TAG = "PowerManagerTest";
+ private Context mContext;
private PowerManager mPm;
private UiDevice mUiDevice;
private Executor mExec = Executors.newSingleThreadExecutor();
@@ -68,21 +76,27 @@
String[] keys, String[] values);
static {
- System.loadLibrary("powermanagertest_jni");
+ if (!RavenwoodRule.isUnderRavenwood()) {
+ System.loadLibrary("powermanagertest_jni");
+ }
}
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood() ? null
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
/**
* Setup any common data for the upcoming tests.
*/
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
MockitoAnnotations.initMocks(this);
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mUiDevice.executeShellCommand("cmd thermalservice override-status 0");
}
@@ -100,6 +114,7 @@
*
* @throws Exception
*/
+ @Test
@SmallTest
public void testPreconditions() throws Exception {
assertNotNull(mPm);
@@ -110,6 +125,7 @@
*
* @throws Exception
*/
+ @Test
@SmallTest
public void testNewWakeLock() throws Exception {
PowerManager.WakeLock wl = mPm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FULL_WAKE_LOCK");
@@ -133,6 +149,7 @@
*
* @throws Exception
*/
+ @Test
@SmallTest
public void testBadNewWakeLock() throws Exception {
final int badFlags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK
@@ -152,6 +169,7 @@
*
* @throws Exception
*/
+ @Test
@SmallTest
public void testWakeLockWithWorkChains() throws Exception {
PowerManager.WakeLock wakeLock = mPm.newWakeLock(
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index b2ffdc0..310baa3 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -17,12 +17,23 @@
package android.os;
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
-public class ProcessTest extends TestCase {
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@IgnoreUnderRavenwood(blockedBy = Process.class)
+public class ProcessTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private static final int BAD_PID = 0;
+ @Test
public void testProcessGetUidFromName() throws Exception {
assertEquals(android.os.Process.SYSTEM_UID, Process.getUidForName("system"));
assertEquals(Process.BLUETOOTH_UID, Process.getUidForName("bluetooth"));
@@ -36,6 +47,7 @@
Process.getUidForName("u3_a100"));
}
+ @Test
public void testProcessGetUidFromNameFailure() throws Exception {
// Failure cases
assertEquals(-1, Process.getUidForName("u2a_foo"));
@@ -51,6 +63,7 @@
* Tests getUidForPid() by ensuring that it returns the correct value when the process queried
* doesn't exist.
*/
+ @Test
public void testGetUidForPidInvalidPid() {
assertEquals(-1, Process.getUidForPid(BAD_PID));
}
@@ -59,6 +72,7 @@
* Tests getParentPid() by ensuring that it returns the correct value when the process queried
* doesn't exist.
*/
+ @Test
public void testGetParentPidInvalidPid() {
assertEquals(-1, Process.getParentPid(BAD_PID));
}
@@ -67,11 +81,13 @@
* Tests getThreadGroupLeader() by ensuring that it returns the correct value when the
* thread queried doesn't exist.
*/
+ @Test
public void testGetThreadGroupLeaderInvalidTid() {
// This function takes a TID instead of a PID but BAD_PID should also be a bad TID.
assertEquals(-1, Process.getThreadGroupLeader(BAD_PID));
}
+ @Test
public void testGetAdvertisedMem() {
assertTrue(Process.getAdvertisedMem() > 0);
assertTrue(Process.getTotalMemory() <= Process.getAdvertisedMem());
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index eff4826..25ce240 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -24,6 +24,8 @@
import static org.junit.Assert.assertEquals;
import android.content.Context;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.system.Os;
import androidx.test.InstrumentationRegistry;
@@ -31,6 +33,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,7 +44,11 @@
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = RedactingFileDescriptor.class)
public class RedactingFileDescriptorTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Context mContext;
private File mFile;
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
index d07187c..593833ec 100644
--- a/core/tests/coretests/src/android/os/TraceTest.java
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -16,24 +16,36 @@
package android.os;
-import android.test.AndroidTestCase;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* This class is used to test the native tracing support. Run this test
* while tracing on the emulator and then run traceview to view the trace.
*/
-public class TraceTest extends AndroidTestCase
-{
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = Trace.class)
+public class TraceTest {
private static final String TAG = "TraceTest";
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private int eMethodCalls = 0;
private int fMethodCalls = 0;
private int gMethodCalls = 0;
+ @Test
public void testNullStrings() {
Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, 42);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, null);
@@ -48,6 +60,7 @@
Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, null);
}
+ @Test
@SmallTest
public void testNativeTracingFromJava()
{
@@ -80,7 +93,8 @@
native void nativeMethod();
native void nativeMethodAndStartTracing();
-
+
+ @Test
@LargeTest
@Suppress // Failing.
public void testMethodTracing()
diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java
index ae6e79a..f34b8fd 100644
--- a/core/tests/coretests/src/android/os/VintfObjectTest.java
+++ b/core/tests/coretests/src/android/os/VintfObjectTest.java
@@ -16,12 +16,27 @@
package android.os;
-import junit.framework.TestCase;
+import static org.junit.Assert.assertTrue;
-public class VintfObjectTest extends TestCase {
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = VintfObject.class)
+public class VintfObjectTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
/**
* Quick check for {@link VintfObject#report VintfObject.report()}.
*/
+ @Test
public void testReport() {
String[] xmls = VintfObject.report();
assertTrue(xmls.length > 0);
diff --git a/core/tests/coretests/src/android/os/WorkSourceParcelTest.java b/core/tests/coretests/src/android/os/WorkSourceParcelTest.java
index 4831606..5f49186 100644
--- a/core/tests/coretests/src/android/os/WorkSourceParcelTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceParcelTest.java
@@ -16,17 +16,25 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import static org.junit.Assert.assertEquals;
-
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@IgnoreUnderRavenwood(reason = "JNI")
public class WorkSourceParcelTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
/**
* END_OF_PARCEL_MARKER is added at the end of Parcel on native or java side on write and
* then read on java or native side on read. This way we can ensure that no extra data
@@ -41,8 +49,11 @@
String[] names, int parcelEndMarker);
static {
- System.loadLibrary("worksourceparceltest_jni");
+ if (!RavenwoodRule.isUnderRavenwood()) {
+ System.loadLibrary("worksourceparceltest_jni");
+ }
}
+
/**
* Confirm that we can pass WorkSource from native to Java.
*/
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 4206fd2..85dc127 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -16,9 +16,19 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
import android.os.WorkSource.WorkChain;
-import junit.framework.TestCase;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@@ -28,7 +38,9 @@
*
* These tests will be moved to CTS when finalized.
*/
-public class WorkSourceTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class WorkSourceTest {
+ @Test
public void testWorkChain_add() {
WorkChain wc1 = new WorkChain();
wc1.addNode(56, null);
@@ -46,6 +58,7 @@
assertEquals(2, wc1.getSize());
}
+ @Test
public void testWorkChain_equalsHashCode() {
WorkChain wc1 = new WorkChain();
WorkChain wc2 = new WorkChain();
@@ -78,6 +91,7 @@
assertFalse(wc1.hashCode() == wc2.hashCode());
}
+ @Test
public void testWorkChain_constructor() {
WorkChain wc1 = new WorkChain();
wc1.addNode(1, "foo")
@@ -91,6 +105,7 @@
assertFalse(wc1.equals(wc2));
}
+ @Test
public void testDiff_workChains() {
WorkSource ws1 = new WorkSource();
ws1.add(50);
@@ -104,6 +119,7 @@
assertFalse(ws2.diff(ws1));
}
+ @Test
public void testEquals_workChains() {
WorkSource ws1 = new WorkSource();
ws1.add(50);
@@ -128,6 +144,7 @@
assertFalse(ws3.equals(ws1));
}
+ @Test
public void testEquals_workChains_nullEmptyAreEquivalent() {
// Construct a WorkSource that has no WorkChains, but whose workChains list
// is non-null.
@@ -145,6 +162,7 @@
assertFalse(ws1.equals(ws2));
}
+ @Test
public void testWorkSourceParcelling() {
WorkSource ws = new WorkSource();
@@ -164,6 +182,7 @@
assertEquals(unparcelled, ws);
}
+ @Test
public void testSet_workChains() {
WorkSource ws1 = new WorkSource();
ws1.add(50);
@@ -193,6 +212,7 @@
assertEquals(1, ws1.getWorkChains().get(0).getSize());
}
+ @Test
public void testSet_nullWorkChain() {
WorkSource ws = new WorkSource();
ws.add(60);
@@ -203,6 +223,7 @@
assertEquals(0, ws.getWorkChains().size());
}
+ @Test
public void testAdd_workChains() {
WorkSource ws = new WorkSource();
ws.createWorkChain().addNode(70, "foo");
@@ -224,6 +245,7 @@
assertEquals(2, workChains.size());
}
+ @Test
public void testSet_noWorkChains() {
WorkSource ws = new WorkSource();
ws.set(10);
@@ -237,6 +259,7 @@
assertEquals("foo", ws2.getPackageName(0));
}
+ @Test
public void testDiffChains_noChanges() {
// WorkSources with no chains.
assertEquals(null, WorkSource.diffChains(new WorkSource(), new WorkSource()));
@@ -254,6 +277,7 @@
assertEquals(null, WorkSource.diffChains(ws2, ws1));
}
+ @Test
public void testDiffChains_noChains() {
// Diffs against a worksource with no chains.
WorkSource ws1 = new WorkSource();
@@ -276,6 +300,7 @@
assertEquals(ws2.getWorkChains(), diffs[1]);
}
+ @Test
public void testDiffChains_onlyAdditionsOrRemovals() {
WorkSource ws1 = new WorkSource();
WorkSource ws2 = new WorkSource();
@@ -302,6 +327,7 @@
}
+ @Test
public void testDiffChains_generalCase() {
WorkSource ws1 = new WorkSource();
WorkSource ws2 = new WorkSource();
@@ -340,6 +366,7 @@
assertEquals(new WorkChain().addNode(2, "tag2"), diffs[1].get(1));
}
+ @Test
public void testGetAttributionId() {
WorkSource ws = new WorkSource();
WorkChain wc1 = ws.createWorkChain();
@@ -355,6 +382,7 @@
assertEquals(100, ws.getAttributionUid());
}
+ @Test
public void testGetAttributionIdWithoutWorkChain() {
WorkSource ws1 = new WorkSource(100);
ws1.add(200);
@@ -365,6 +393,7 @@
assertEquals(100, ws2.getAttributionUid());
}
+ @Test
public void testGetAttributionWhenEmpty() {
WorkSource ws = new WorkSource();
assertEquals(-1, ws.getAttributionUid());
@@ -374,6 +403,7 @@
assertNull(wc.getAttributionTag());
}
+ @Test
public void testGetAttributionTag() {
WorkSource ws1 = new WorkSource();
WorkChain wc = ws1.createWorkChain();
@@ -383,6 +413,7 @@
assertEquals("tag", wc.getAttributionTag());
}
+ @Test
public void testRemove_fromChainedWorkSource() {
WorkSource ws1 = new WorkSource();
ws1.createWorkChain().addNode(50, "foo");
@@ -403,6 +434,7 @@
assertEquals(75, ws1.getWorkChains().get(0).getAttributionUid());
}
+ @Test
public void testRemove_fromSameWorkSource() {
WorkSource ws1 = new WorkSource(50, "foo");
WorkSource ws2 = ws1;
@@ -414,6 +446,7 @@
assertEquals("foo", ws1.getPackageName(0));
}
+ @Test
public void testTransferWorkChains() {
WorkSource ws1 = new WorkSource();
WorkChain wc1 = ws1.createWorkChain().addNode(100, "tag");
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 8207c9e..b70f290 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -29,7 +29,9 @@
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -39,6 +41,8 @@
import com.android.internal.os.BinderInternal.CallSession;
import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,13 +59,22 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
@Presubmit
+@IgnoreUnderRavenwood(blockedBy = BinderCallsStats.class)
public class BinderCallsStatsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final int WORKSOURCE_UID = Process.FIRST_APPLICATION_UID;
private static final int CALLING_UID = 2;
private static final int REQUEST_SIZE = 2;
private static final int REPLY_SIZE = 3;
private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true);
- private final TestHandler mHandler = new TestHandler();
+ private TestHandler mHandler;
+
+ @Before
+ public void setUp() {
+ mHandler = new TestHandler();
+ }
@Test
public void testDetailedOff() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java b/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
index e4597b5..a1b80d2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
@@ -16,11 +16,19 @@
package com.android.internal.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.os.Binder;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@@ -29,7 +37,8 @@
/**
* Tests for {@link BinderCallHeavyHitterWatcher}.
*/
-public final class BinderHeavyHitterTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public final class BinderHeavyHitterTest {
private boolean mListenerNotified = false;
@@ -114,6 +123,7 @@
}
}
+ @Test
public void testPositive() throws Exception {
BinderCallHeavyHitterWatcher watcher = BinderCallHeavyHitterWatcher.getInstance();
try {
@@ -142,6 +152,7 @@
}
}
+ @Test
public void testNegative() throws Exception {
BinderCallHeavyHitterWatcher watcher = BinderCallHeavyHitterWatcher.getInstance();
try {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index 7bd53b9..31b55e6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -23,7 +23,9 @@
import static org.junit.Assert.assertEquals;
import android.os.Binder;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;
@@ -36,6 +38,7 @@
import com.android.internal.os.BinderLatencyProto.Dims;
import com.android.internal.os.BinderLatencyProto.RepeatedApiStats;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +50,11 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
@Presubmit
+@IgnoreUnderRavenwood(blockedBy = BinderLatencyObserver.class)
public class BinderLatencyObserverTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void testLatencyCollectionWithMultipleClasses() {
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
index e9f6450..5f02f04 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
@@ -20,11 +20,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import android.content.Context;
import android.os.FileUtils;
import android.util.IntArray;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -102,9 +100,8 @@
private boolean mHasError;
@Before
- public void setUp() {
- Context context = InstrumentationRegistry.getContext();
- mStatsDirectory = context.getDir("binder_logs", Context.MODE_PRIVATE);
+ public void setUp() throws Exception {
+ mStatsDirectory = Files.createTempDirectory("BinderfsStatsReaderTest").toFile();
mFreezerBinderAsyncThreshold = 1024;
mValidPids = IntArray.fromArray(new int[]{14505, 14461, 542, 540}, 4);
mStatsPids = new IntArray();
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java b/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
index 7f054d1..2da3873 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
@@ -16,11 +16,8 @@
package com.android.internal.os;
-import static androidx.test.InstrumentationRegistry.getContext;
-
import static com.google.common.truth.Truth.assertThat;
-import android.content.Context;
import android.os.FileUtils;
import androidx.test.runner.AndroidJUnit4;
@@ -31,6 +28,7 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
@RunWith(AndroidJUnit4.class)
public class CpuScalingPolicyReaderTest {
@@ -38,7 +36,7 @@
@Before
public void setup() throws IOException {
- File testDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ File testDir = Files.createTempDirectory("CpuScalingPolicyReaderTest").toFile();
FileUtils.deleteContents(testDir);
File policy0 = new File(testDir, "policy0");
diff --git a/core/tests/coretests/src/com/android/internal/os/DebugTest.java b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
index 2a8a857..4371f26 100644
--- a/core/tests/coretests/src/com/android/internal/os/DebugTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
@@ -16,14 +16,22 @@
package com.android.internal.os;
+import static org.junit.Assert.assertTrue;
+
import android.os.Debug;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
@SmallTest
-public class DebugTest extends TestCase {
+@IgnoreUnderRavenwood(reason = "Requires ART support")
+public class DebugTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private final static String EXPECTED_GET_CALLER =
"com\\.android\\.internal\\.os\\.DebugTest\\.testGetCaller:\\d\\d";
@@ -39,6 +47,7 @@
return Debug.getCaller();
}
+ @Test
public void testGetCaller() {
assertTrue(callDepth0().matches(EXPECTED_GET_CALLER));
}
@@ -62,6 +71,7 @@
return callDepth2();
}
+ @Test
public void testGetCallers() {
assertTrue(callDepth1().matches(EXPECTED_GET_CALLERS));
}
@@ -69,6 +79,7 @@
/**
* Regression test for b/31943543. Note: must be run under CheckJNI to detect the issue.
*/
+ @Test
public void testGetMemoryInfo() {
Debug.MemoryInfo info = new Debug.MemoryInfo();
Debug.getMemoryInfo(-1, info);
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index cbd2ba4..1d8628d 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.os.FileUtils;
import android.os.SystemClock;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -31,6 +33,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -57,7 +60,11 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuProcStringReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File mRoot;
private File mTestDir;
private File mTestFile;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
index b45f8d2..a57a400 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
@@ -24,13 +24,16 @@
import static java.util.stream.Collectors.toList;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -44,7 +47,10 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuThreadReaderDiffTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private MockitoSession mMockingSessions;
@Mock KernelCpuThreadReader mMockReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index d43989c..8c5e3d0 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -22,6 +22,8 @@
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
@@ -29,6 +31,7 @@
import com.android.internal.os.KernelCpuThreadReader.ProcessCpuUsage;
import com.android.internal.os.KernelCpuThreadReader.ThreadCpuUsage;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +50,10 @@
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuThreadReaderEndToEndTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private static final int TIMED_NUM_SAMPLES = 5;
private static final int TIMED_START_MILLIS = 500;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index c3e4014..7eac2a3 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -25,7 +25,9 @@
import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -33,6 +35,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,7 +51,11 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuThreadReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File mProcDirectory;
@Before
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index 2ccd74e..d35e0fc 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -21,6 +21,8 @@
import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -31,6 +33,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -51,7 +54,11 @@
*/
@SmallTest
@RunWith(Parameterized.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuUidActiveTimeReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File mTestDir;
private File mTestFile;
private KernelCpuUidActiveTimeReader mReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
index bda21c6..610e6ae 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
@@ -23,11 +23,15 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
import org.junit.runner.RunWith;
import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
@@ -50,7 +54,11 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuUidBpfMapReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Random mRand = new Random(12345);
private KernelCpuUidTestBpfMapReader mReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index a0dab28..8807de0 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -32,6 +34,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -52,7 +55,11 @@
*/
@SmallTest
@RunWith(Parameterized.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuUidClusterTimeReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File mTestDir;
private File mTestFile;
private KernelCpuUidClusterTimeReader mReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index 783f264..b730344 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -33,6 +35,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -55,7 +58,11 @@
*/
@SmallTest
@RunWith(Parameterized.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuUidFreqTimeReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File mTestDir;
private File mTestFile;
private KernelCpuUidFreqTimeReader mReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 1da1a90..6507226 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -33,6 +35,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,7 +52,11 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelCpuUidUserSysTimeReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private File mTestDir;
private File mTestFile;
private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
index 60dac85..ad5186e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
@@ -16,12 +16,18 @@
package com.android.internal.os;
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.LongSparseLongArray;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
-
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.io.BufferedReader;
@@ -29,12 +35,17 @@
/**
* Tests for KernelMemoryBandwidthStats parsing and delta calculation, based on memory_state_time.
*/
-public class KernelMemoryBandwidthStatsTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
+public class KernelMemoryBandwidthStatsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
/**
* Standard example of parsing stats.
* @throws Exception
*/
+ @Test
@SmallTest
public void testParseStandard() throws Exception {
KernelMemoryBandwidthStats stats = new KernelMemoryBandwidthStats();
@@ -68,6 +79,7 @@
* zero.
* @throws Exception
*/
+ @Test
@SmallTest
public void testParseBackwards() throws Exception {
KernelMemoryBandwidthStats zeroStats = new KernelMemoryBandwidthStats();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index 2de800b..f42d26d 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -19,9 +19,13 @@
import static com.google.common.truth.Truth.assertThat;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +35,10 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelSingleProcessCpuThreadReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
public void getProcessCpuUsage() throws IOException {
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 74ab644..120a4de 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -23,6 +23,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -30,6 +32,7 @@
import com.android.internal.os.KernelSingleUidTimeReader.Injector;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -43,7 +46,11 @@
@SmallTest
@RunWith(Parameterized.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class KernelSingleUidTimeReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private final static int TEST_UID = 2222;
private final static int TEST_FREQ_COUNT = 5;
diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
index cb8a62c..632dce0 100644
--- a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
@@ -16,9 +16,13 @@
package com.android.internal.os;
-import androidx.test.filters.Suppress;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
-import junit.framework.TestCase;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -28,11 +32,12 @@
import java.util.List;
// this test causes a IllegalAccessError: superclass not accessible
-@Suppress
-public class LoggingPrintStreamTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class LoggingPrintStreamTest {
TestPrintStream out = new TestPrintStream();
+ @Test
public void testPrintException() {
@SuppressWarnings("ThrowableInstanceNeverThrown")
Throwable t = new Throwable("Ignore me.");
@@ -47,6 +52,7 @@
assertEquals(Arrays.asList(lines), out.lines);
}
+ @Test
public void testPrintObject() {
Object o = new Object();
out.print(4);
@@ -56,6 +62,7 @@
assertEquals(Arrays.asList("4" + o + "2"), out.lines);
}
+ @Test
public void testPrintlnObject() {
Object o = new Object();
out.print(4);
@@ -65,6 +72,7 @@
assertEquals(Arrays.asList("4" + o, "2"), out.lines);
}
+ @Test
public void testPrintf() {
out.printf("Name: %s\nEmployer: %s", "Bob", "Google");
assertEquals(Arrays.asList("Name: Bob"), out.lines);
@@ -72,6 +80,7 @@
assertEquals(Arrays.asList("Name: Bob", "Employer: Google"), out.lines);
}
+ @Test
public void testPrintInt() {
out.print(4);
out.print(2);
@@ -80,12 +89,14 @@
assertEquals(Collections.singletonList("42"), out.lines);
}
+ @Test
public void testPrintlnInt() {
out.println(4);
out.println(2);
assertEquals(Arrays.asList("4", "2"), out.lines);
}
+ @Test
public void testPrintCharArray() {
out.print("Foo\nBar\nTee".toCharArray());
assertEquals(Arrays.asList("Foo", "Bar"), out.lines);
@@ -93,6 +104,7 @@
assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines);
}
+ @Test
public void testPrintString() {
out.print("Foo\nBar\nTee");
assertEquals(Arrays.asList("Foo", "Bar"), out.lines);
@@ -100,22 +112,26 @@
assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines);
}
+ @Test
public void testPrintlnCharArray() {
out.println("Foo\nBar\nTee".toCharArray());
assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines);
}
+ @Test
public void testPrintlnString() {
out.println("Foo\nBar\nTee");
assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines);
}
+ @Test
public void testPrintlnStringWithBufferedData() {
out.print(5);
out.println("Foo\nBar\nTee");
assertEquals(Arrays.asList("5Foo", "Bar", "Tee"), out.lines);
}
+ @Test
public void testAppend() {
out.append("Foo\n")
.append('4')
@@ -125,6 +141,7 @@
assertEquals(Arrays.asList("Foo", "4", "a"), out.lines);
}
+ @Test
public void testMultiByteCharactersSpanningBuffers() throws Exception {
// assume 3*1000 bytes won't fit in LoggingPrintStream's internal buffer
StringBuilder builder = new StringBuilder();
@@ -138,6 +155,7 @@
assertEquals(Arrays.asList(expected), out.lines);
}
+ @Test
public void testWriteOneByteAtATimeMultibyteCharacters() throws Exception {
String expected = " \u20AC \u20AC \u20AC \u20AC ";
for (byte b : expected.getBytes()) {
@@ -147,6 +165,7 @@
assertEquals(Arrays.asList(expected), out.lines);
}
+ @Test
public void testWriteByteArrayAtATimeMultibyteCharacters() throws Exception {
String expected = " \u20AC \u20AC \u20AC \u20AC ";
out.write(expected.getBytes());
@@ -154,6 +173,7 @@
assertEquals(Arrays.asList(expected), out.lines);
}
+ @Test
public void testWriteWithOffsetsMultibyteCharacters() throws Exception {
String expected = " \u20AC \u20AC \u20AC \u20AC ";
byte[] bytes = expected.getBytes();
@@ -167,6 +187,7 @@
assertEquals(Arrays.asList(expected), out.lines);
}
+ @Test
public void testWriteFlushesOnNewlines() throws Exception {
String a = " \u20AC \u20AC ";
String b = " \u20AC \u20AC ";
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index faccf1a..c9536b9 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -22,16 +22,22 @@
import android.os.BadParcelableException;
import android.os.Parcel;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@IgnoreUnderRavenwood(blockedBy = LongArrayMultiStateCounter.class)
public class LongArrayMultiStateCounterTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
public void setStateAndUpdateValue() {
@@ -51,6 +57,16 @@
}
@Test
+ public void setValue() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+
+ counter.setValues(0, new long[]{1, 2, 3, 4});
+ counter.setValues(1, new long[]{5, 6, 7, 8});
+ assertCounts(counter, 0, new long[]{1, 2, 3, 4});
+ assertCounts(counter, 1, new long[]{5, 6, 7, 8});
+ }
+
+ @Test
public void setEnabled() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
counter.setState(0, 1000);
diff --git a/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
index 3413753..e064e74 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
@@ -22,16 +22,22 @@
import android.os.BadParcelableException;
import android.os.Parcel;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@IgnoreUnderRavenwood(blockedBy = LongMultiStateCounterTest.class)
public class LongMultiStateCounterTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
public void setStateAndUpdateValue() {
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 48baf09..dfb5cc3 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -23,6 +23,7 @@
import android.os.Looper;
import android.os.Message;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +31,7 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +42,9 @@
@RunWith(AndroidJUnit4.class)
@Presubmit
public final class LooperStatsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private HandlerThread mThreadFirst;
private HandlerThread mThreadSecond;
private Handler mHandlerFirst;
diff --git a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
index 7951270..0742052 100644
--- a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
@@ -43,14 +43,12 @@
assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
ByteArrayOutputStream out = new ByteArrayOutputStream();
- monotonicClock.writeXml(out, Xml.newFastSerializer());
- String xml = out.toString();
- assertThat(xml).contains("timeshift=\"1234\"");
+ monotonicClock.writeXml(out, Xml.newBinarySerializer());
mClock.realtime = 42;
MonotonicClock newMonotonicClock = new MonotonicClock(0, mClock);
- newMonotonicClock.readXml(new ByteArrayInputStream(out.toByteArray()),
- Xml.newFastPullParser());
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ newMonotonicClock.readXml(in, Xml.newBinaryPullParser());
mClock.realtime = 2000;
assertThat(newMonotonicClock.monotonicTime()).isEqualTo(1234 - 42 + 2000);
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 77202d1..c0f0714 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -27,6 +27,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -38,6 +40,7 @@
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,7 +53,10 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = PowerProfile.class)
public class PowerProfileTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
static final String TAG_TEST_MODEM = "test-modem";
static final String ATTR_NAME = "name";
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index 29da231..b99e202 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -21,17 +21,23 @@
import android.os.BatteryConsumer;
import android.os.Parcel;
import android.os.PersistableBundle;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class PowerStatsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private PowerStats.DescriptorRegistry mRegistry;
private PowerStats.Descriptor mDescriptor;
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
index c3d40eb..f61fc7c 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
@@ -18,11 +18,9 @@
import static org.junit.Assert.assertTrue;
-import android.content.Context;
import android.os.FileUtils;
import android.util.IntArray;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -45,9 +43,8 @@
private ArrayList<int[]> mPids = new ArrayList<>();
@Before
- public void setUp() {
- Context context = InstrumentationRegistry.getContext();
- mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ public void setUp() throws Exception {
+ mProcDirectory = Files.createTempDirectory("ProcLocksReaderTest").toFile();
}
@After
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
index e97caf8..3e4f34d 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
@@ -18,10 +18,8 @@
import static org.junit.Assert.assertEquals;
-import android.content.Context;
import android.os.FileUtils;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -41,9 +39,8 @@
private File mProcDirectory;
@Before
- public void setUp() {
- Context context = InstrumentationRegistry.getContext();
- mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ public void setUp() throws Exception {
+ mProcDirectory = Files.createTempDirectory("ProcStatsUtilTest").toFile();
}
@After
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index 9db3f8a..a706350 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -20,15 +20,16 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
-import android.content.Context;
import android.os.FileUtils;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,14 +40,16 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ProcTimeInStateReader.class)
public class ProcTimeInStateReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private File mProcDirectory;
@Before
- public void setUp() {
- Context context = InstrumentationRegistry.getContext();
- mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ public void setUp() throws Exception {
+ mProcDirectory = Files.createTempDirectory("ProcTimeInStateReaderTest").toFile();
}
@After
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java b/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java
index 81cc9d8..d11c500 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java
@@ -18,15 +18,23 @@
import static com.google.common.truth.Truth.assertThat;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@SmallTest
@RunWith(JUnit4.class)
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class ProcessCpuTrackerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void testGetCpuTime() throws Exception {
final ProcessCpuTracker tracker = new ProcessCpuTracker(false);
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
index 85eafc5..84c93c2 100644
--- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -20,10 +20,8 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
-import android.content.Context;
import android.os.FileUtils;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,7 +46,6 @@
@RunWith(AndroidJUnit4.class)
public class StoragedUidIoStatsReaderTest {
- private File mRoot;
private File mTestDir;
private File mTestFile;
// private Random mRand = new Random();
@@ -57,15 +54,10 @@
@Mock
private StoragedUidIoStatsReader.Callback mCallback;
- private Context getContext() {
- return InstrumentationRegistry.getContext();
- }
-
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
- mRoot = getContext().getFilesDir();
+ mTestDir = Files.createTempDirectory("StoragedUidIoStatsReaderTest").toFile();
mTestFile = new File(mTestDir, "test.file");
mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(mTestFile.getAbsolutePath());
}
@@ -73,10 +65,8 @@
@After
public void tearDown() throws Exception {
FileUtils.deleteContents(mTestDir);
- FileUtils.deleteContents(mRoot);
}
-
/**
* Tests that reading will never call the callback.
*/
diff --git a/core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java b/core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java
index e09cfd2..ae2ef0cb 100644
--- a/core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java
@@ -34,10 +34,13 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.view.Display;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Arrays;
@@ -46,7 +49,10 @@
* Test class for {@link EnergyConsumerStats}.
*/
@SmallTest
+@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class EnergyConsumerStatsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
public void testConstruction() {
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index f0ed6ee..e346b51 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,4 +1,4 @@
xutan@google.com
# Give submodule owners in shell resource approval
-per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com
+per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b1b196d..fe65fdd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,6 +16,7 @@
package com.android.wm.shell;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -168,6 +169,13 @@
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
+ /** Overlay surface for home root task */
+ private final SurfaceControl mHomeTaskOverlayContainer = new SurfaceControl.Builder()
+ .setName("home_task_overlay_container")
+ .setContainerLayer()
+ .setHidden(false)
+ .build();
+
/**
* In charge of showing compat UI. Can be {@code null} if the device doesn't support size
* compat or if this isn't the main {@link ShellTaskOrganizer}.
@@ -428,6 +436,14 @@
}
}
+ /**
+ * Returns a surface which can be used to attach overlays to the home root task
+ */
+ @NonNull
+ public SurfaceControl getHomeTaskOverlayContainer() {
+ return mHomeTaskOverlayContainer;
+ }
+
@Override
public void addStartingWindow(StartingWindowInfo info) {
if (mStartingWindow != null) {
@@ -485,6 +501,15 @@
if (mUnfoldAnimationController != null) {
mUnfoldAnimationController.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
+
+ if (info.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Adding overlay to home task");
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setLayer(mHomeTaskOverlayContainer, Integer.MAX_VALUE);
+ t.reparent(mHomeTaskOverlayContainer, info.getLeash());
+ t.apply();
+ }
+
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
notifyCompatUI(info.getTaskInfo(), listener);
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskAdded(info.getTaskInfo()));
@@ -579,6 +604,12 @@
notifyCompatUI(taskInfo, null /* taskListener */);
// Notify the recent tasks that a task has been removed
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.reparent(mHomeTaskOverlayContainer, null);
+ t.apply();
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface");
+ }
if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) {
// Preemptively clean up the leash only if shell transitions are not enabled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 689323b..7f34ee0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -78,34 +78,37 @@
mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- if (mExpandedBubble != null && mExpandedBubble.getBubbleBarExpandedView() != null) {
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev != null) {
// We need to be Z ordered on top in order for alpha animations to work.
- mExpandedBubble.getBubbleBarExpandedView().setSurfaceZOrderedOnTop(true);
- mExpandedBubble.getBubbleBarExpandedView().setAnimating(true);
+ bbev.setSurfaceZOrderedOnTop(true);
+ bbev.setAnimating(true);
}
}
@Override
public void onAnimationEnd(Animator animation) {
- if (mExpandedBubble != null && mExpandedBubble.getBubbleBarExpandedView() != null) {
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev != null) {
// The surface needs to be Z ordered on top for alpha values to work on the
// TaskView, and if we're temporarily hidden, we are still on the screen
// with alpha = 0f until we animate back. Stay Z ordered on top so the alpha
// = 0f remains in effect.
if (mIsExpanded) {
- mExpandedBubble.getBubbleBarExpandedView().setSurfaceZOrderedOnTop(false);
+ bbev.setSurfaceZOrderedOnTop(false);
}
- mExpandedBubble.getBubbleBarExpandedView().setContentVisibility(mIsExpanded);
- mExpandedBubble.getBubbleBarExpandedView().setAnimating(false);
+ bbev.setContentVisibility(mIsExpanded);
+ bbev.setAnimating(false);
}
}
});
mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
- if (mExpandedBubble != null && mExpandedBubble.getBubbleBarExpandedView() != null) {
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev != null) {
float alpha = (float) valueAnimator.getAnimatedValue();
- mExpandedBubble.getBubbleBarExpandedView().setTaskViewAlpha(alpha);
- mExpandedBubble.getBubbleBarExpandedView().setAlpha(alpha);
+ bbev.setTaskViewAlpha(alpha);
+ bbev.setAlpha(alpha);
}
});
}
@@ -116,11 +119,8 @@
public void animateExpansion(BubbleViewProvider expandedBubble,
@Nullable Runnable afterAnimation) {
mExpandedBubble = expandedBubble;
- if (mExpandedBubble == null) {
- return;
- }
- BubbleBarExpandedView bev = mExpandedBubble.getBubbleBarExpandedView();
- if (bev == null) {
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
return;
}
mIsExpanded = true;
@@ -129,11 +129,11 @@
mExpandedViewContainerMatrix.setScaleY(0f);
updateExpandedView();
- bev.setAnimating(true);
- bev.setContentVisibility(false);
- bev.setAlpha(0f);
- bev.setTaskViewAlpha(0f);
- bev.setVisibility(VISIBLE);
+ bbev.setAnimating(true);
+ bbev.setContentVisibility(false);
+ bbev.setAlpha(0f);
+ bbev.setTaskViewAlpha(0f);
+ bbev.setVisibility(VISIBLE);
// Set the pivot point for the scale, so the view animates out from the bubble bar.
Point bubbleBarPosition = mPositioner.getBubbleBarPosition();
@@ -143,7 +143,7 @@
bubbleBarPosition.x,
bubbleBarPosition.y);
- bev.setAnimationMatrix(mExpandedViewContainerMatrix);
+ bbev.setAnimationMatrix(mExpandedViewContainerMatrix);
mExpandedViewAlphaAnimator.start();
@@ -156,13 +156,12 @@
AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
mScaleInSpringConfig)
.addUpdateListener((target, values) -> {
- mExpandedBubble.getBubbleBarExpandedView().setAnimationMatrix(
- mExpandedViewContainerMatrix);
+ bbev.setAnimationMatrix(mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
- bev.setAnimationMatrix(null);
+ bbev.setAnimationMatrix(null);
updateExpandedView();
- bev.setSurfaceZOrderedOnTop(false);
+ bbev.setSurfaceZOrderedOnTop(false);
if (afterAnimation != null) {
afterAnimation.run();
}
@@ -177,7 +176,8 @@
*/
public void animateCollapse(Runnable endRunnable) {
mIsExpanded = false;
- if (mExpandedBubble == null || mExpandedBubble.getBubbleBarExpandedView() == null) {
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
Log.w(TAG, "Trying to animate collapse without a bubble");
return;
}
@@ -196,17 +196,10 @@
EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT),
mScaleOutSpringConfig)
.addUpdateListener((target, values) -> {
- if (mExpandedBubble != null
- && mExpandedBubble.getBubbleBarExpandedView() != null) {
- mExpandedBubble.getBubbleBarExpandedView().setAnimationMatrix(
- mExpandedViewContainerMatrix);
- }
+ bbev.setAnimationMatrix(mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
- if (mExpandedBubble != null
- && mExpandedBubble.getBubbleBarExpandedView() != null) {
- mExpandedBubble.getBubbleBarExpandedView().setAnimationMatrix(null);
- }
+ bbev.setAnimationMatrix(null);
if (endRunnable != null) {
endRunnable.run();
}
@@ -223,12 +216,20 @@
mExpandedViewAlphaAnimator.cancel();
}
+ private @Nullable BubbleBarExpandedView getExpandedView() {
+ BubbleViewProvider bubble = mExpandedBubble;
+ if (bubble != null) {
+ return bubble.getBubbleBarExpandedView();
+ }
+ return null;
+ }
+
private void updateExpandedView() {
- if (mExpandedBubble == null || mExpandedBubble.getBubbleBarExpandedView() == null) {
+ BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
Log.w(TAG, "Trying to update the expanded view without a bubble");
return;
}
- BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView();
boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index ef763ec..afd3b14 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.LayoutInflater;
@@ -227,9 +228,12 @@
}
private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
+ final Intent intent = taskInfo.baseIntent;
return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton
&& (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
|| taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled)
+ && Intent.ACTION_MAIN.equals(intent.getAction())
+ && intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
index deb7c6d..1385f42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -1,3 +1,7 @@
# WM shell sub-module desktop owners
atsjenk@google.com
+jorgegil@google.com
madym@google.com
+nmusgrave@google.com
+pbdr@google.com
+tkachenkoi@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index a3803ed..8a0eea0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -2,3 +2,6 @@
atsjenk@google.com
jorgegil@google.com
madym@google.com
+nmusgrave@google.com
+pbdr@google.com
+tkachenkoi@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
index 644a6a5..7f4a8f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
@@ -16,6 +16,7 @@
package com.android.wm.shell.transition;
+import android.view.SurfaceControl;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
@@ -42,6 +43,13 @@
*/
IBinder getShellApplyToken() = 3;
- /** Set listener that will receive callbacks about transitions involving home activity */
+ /**
+ * Set listener that will receive callbacks about transitions involving home activity.
+ */
oneway void setHomeTransitionListener(in IHomeTransitionListener listener) = 4;
+
+ /**
+ * Returns a container surface for the home root task.
+ */
+ SurfaceControl getHomeTaskOverlayContainer() = 5;
}
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 b98762d..af69b52 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
@@ -64,7 +64,6 @@
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
import androidx.annotation.BinderThread;
@@ -72,6 +71,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -172,7 +172,7 @@
/** Transition to animate task to desktop. */
public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15;
- private final WindowOrganizer mOrganizer;
+ private final ShellTaskOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
@@ -264,7 +264,7 @@
public Transitions(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
- @NonNull WindowOrganizer organizer,
+ @NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
@NonNull ShellExecutor mainExecutor,
@@ -280,7 +280,7 @@
@NonNull ShellInit shellInit,
@Nullable ShellCommandHandler shellCommandHandler,
@NonNull ShellController shellController,
- @NonNull WindowOrganizer organizer,
+ @NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
@NonNull ShellExecutor mainExecutor,
@@ -1240,6 +1240,10 @@
}
}
+ private SurfaceControl getHomeTaskOverlayContainer() {
+ return mOrganizer.getHomeTaskOverlayContainer();
+ }
+
/**
* Interface for a callback that must be called after a TransitionHandler finishes playing an
* animation.
@@ -1470,6 +1474,17 @@
listener);
});
}
+
+ @Override
+ public SurfaceControl getHomeTaskOverlayContainer() {
+ SurfaceControl[] result = new SurfaceControl[1];
+ executeRemoteCallWithTaskPermission(mTransitions, "getHomeTaskOverlayContainer",
+ (controller) -> {
+ result[0] = controller.getHomeTaskOverlayContainer();
+ }, true /* blocking */);
+ // Return a copy as writing to parcel releases the original surface
+ return new SurfaceControl(result[0], "Transitions.HomeOverlay");
+ }
}
private class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index deebad5..d718e15 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -9,3 +9,6 @@
chenghsiuchang@google.com
atsjenk@google.com
jorgegil@google.com
+nmusgrave@google.com
+pbdr@google.com
+tkachenkoi@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 0652939..9fe2cb1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.compatui;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.hardware.usb.UsbManager.ACTION_USB_STATE;
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -33,6 +36,7 @@
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -108,7 +112,7 @@
MockitoAnnotations.initMocks(this);
mExecutor = new TestShellExecutor();
mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- false, /* topActivityBoundsLetterboxed */ true);
+ false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,
@@ -179,7 +183,7 @@
// No diff
clearInvocations(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- true, /* topActivityBoundsLetterboxed */ true);
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true));
verify(mWindowManager, never()).updateSurfacePosition();
@@ -200,7 +204,24 @@
clearInvocations(mWindowManager);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- false, /* topActivityBoundsLetterboxed */ true);
+ false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+ assertFalse(
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
+ verify(mWindowManager).release();
+
+ // Recreate button
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+ assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
+
+ verify(mWindowManager).release();
+ verify(mWindowManager).createLayout(/* canShow= */ true);
+
+ // Change has no launcher category and is not main intent, dispose the component
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_USB_STATE, "");
assertFalse(
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mWindowManager).release();
@@ -217,7 +238,7 @@
// inflated
clearInvocations(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- false, /* topActivityBoundsLetterboxed */ true);
+ false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
verify(mWindowManager, never()).inflateLayout();
@@ -225,7 +246,7 @@
// Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
clearInvocations(mWindowManager);
taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- true, /* topActivityBoundsLetterboxed */ true);
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
verify(mWindowManager).inflateLayout();
@@ -304,7 +325,7 @@
clearInvocations(mWindowManager);
spyOn(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
- true, /* topActivityBoundsLetterboxed */ true);
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
// User aspect ratio settings button has not yet been shown.
doReturn(false).when(mUserAspectRatioButtonShownChecker).get();
@@ -378,7 +399,7 @@
}
private static TaskInfo createTaskInfo(boolean eligibleForUserAspectRatioButton,
- boolean topActivityBoundsLetterboxed) {
+ boolean topActivityBoundsLetterboxed, String action, String category) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton =
@@ -386,6 +407,7 @@
taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed = topActivityBoundsLetterboxed;
taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK;
taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity");
+ taskInfo.baseIntent = new Intent(action).addCategory(category);
return taskInfo;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 50802c3..66efa02 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -40,12 +40,12 @@
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
-import android.window.WindowOrganizer;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
@@ -68,7 +68,7 @@
@RunWith(AndroidJUnit4.class)
public class HomeTransitionObserverTest extends ShellTestCase {
- private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
+ private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 01c9bd0..e22bf3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -87,7 +87,6 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -98,6 +97,7 @@
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
@@ -130,7 +130,7 @@
@RunWith(AndroidJUnit4.class)
public class ShellTransitionTests extends ShellTestCase {
- private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
+ private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 79a7357..4741170 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -79,14 +79,6 @@
"external/skia/src/core",
],
- product_variables: {
- eng: {
- lto: {
- never: true,
- },
- },
- },
-
target: {
android: {
include_dirs: [
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index 96a11ee..5b39f4e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -112,26 +112,6 @@
}
/**
- * Shows restricted setting dialog.
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public static void sendShowRestrictedSettingDialogIntent(Context context,
- String packageName, int uid) {
- final Intent intent = getShowRestrictedSettingsIntent(packageName, uid);
- context.startActivity(intent);
- }
-
- /**
- * Gets restricted settings dialog intent.
- */
- private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) {
- final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- return intent;
- }
-
- /**
* Checks if current user is profile or not
*/
@RequiresApi(Build.VERSION_CODES.M)
@@ -238,4 +218,35 @@
+ '}';
}
}
+
+
+ /**
+ * Shows restricted setting dialog.
+ *
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ public static void sendShowRestrictedSettingDialogIntent(Context context,
+ String packageName, int uid) {
+ final Intent intent = getShowRestrictedSettingsIntent(packageName, uid);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Gets restricted settings dialog intent.
+ *
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) {
+ final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ return intent;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 4454b71..0237446 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -77,6 +77,9 @@
ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN);
}
+
+ ECM_KEYS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
+ ECM_KEYS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index db2a6ec..50e3bd0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -96,12 +96,29 @@
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param restriction The key identifying the setting
+ * @param packageName the package to check the restriction for
+ * @param uid the uid of the package
+ */
+ public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
+ mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ }
+
@Override
public void setEnabled(boolean enabled) {
if (enabled && isDisabledByAdmin()) {
mHelper.setDisabledByAdmin(null);
return;
}
+
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ return;
+ }
+
super.setEnabled(enabled);
}
@@ -111,16 +128,14 @@
}
}
- public void setDisabledByAppOps(boolean disabled) {
- if (mHelper.setDisabledByAppOps(disabled)) {
- notifyChanged();
- }
- }
-
public boolean isDisabledByAdmin() {
return mHelper.isDisabledByAdmin();
}
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
public int getUid() {
return mHelper != null ? mHelper.uid : Process.INVALID_UID;
}
@@ -128,4 +143,16 @@
public String getPackageName() {
return mHelper != null ? mHelper.packageName : null;
}
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ public void setDisabledByAppOps(boolean disabled) {
+ if (mHelper.setDisabledByAppOps(disabled)) {
+ notifyChanged();
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 29ea25e..a479269 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -17,10 +17,12 @@
package com.android.settingslib;
import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.UserHandle;
@@ -52,7 +54,8 @@
private String mAttrUserRestriction = null;
private boolean mDisabledSummary = false;
- private boolean mDisabledByAppOps;
+ private boolean mDisabledByEcm;
+ private Intent mDisabledByEcmIntent = null;
public RestrictedPreferenceHelper(Context context, Preference preference,
AttributeSet attrs, String packageName, int uid) {
@@ -101,7 +104,7 @@
* Modify PreferenceViewHolder to add padlock if restriction is disabled.
*/
public void onBindViewHolder(PreferenceViewHolder holder) {
- if (mDisabledByAdmin || mDisabledByAppOps) {
+ if (mDisabledByAdmin || mDisabledByEcm) {
holder.itemView.setEnabled(true);
}
if (mDisabledSummary) {
@@ -112,7 +115,7 @@
: mContext.getString(R.string.disabled_by_admin_summary_text);
if (mDisabledByAdmin) {
summaryView.setText(disabledText);
- } else if (mDisabledByAppOps) {
+ } else if (mDisabledByEcm) {
summaryView.setText(R.string.disabled_by_app_ops_text);
} else if (TextUtils.equals(disabledText, summaryView.getText())) {
// It's previously set to disabled text, clear it.
@@ -144,7 +147,12 @@
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
return true;
}
- if (mDisabledByAppOps) {
+ if (mDisabledByEcm) {
+ if (android.security.Flags.extendEcmToAllSettings()) {
+ mContext.startActivity(mDisabledByEcmIntent);
+ return true;
+ }
+
RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
uid);
return true;
@@ -174,6 +182,20 @@
}
/**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param restriction The key identifying the setting
+ * @param packageName the package to check the restriction for
+ * @param uid the uid of the package
+ */
+ public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
+ updatePackageDetails(packageName, uid);
+ Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
+ mContext, restriction, uid, packageName);
+ setDisabledByEcm(intent);
+ }
+
+ /**
* @return EnforcedAdmin if we have been passed the restriction in the xml.
*/
public EnforcedAdmin checkRestrictionEnforced() {
@@ -211,10 +233,19 @@
return changed;
}
- public boolean setDisabledByAppOps(boolean disabled) {
+ /**
+ * Disable the preference based on the passed in Intent
+ * @param disabledIntent The intent which is started when the user clicks the disabled
+ * preference. If it is {@code null}, then this preference will be enabled. Otherwise, it will
+ * be disabled.
+ * @return true if the disabled state was changed.
+ */
+ public boolean setDisabledByEcm(Intent disabledIntent) {
+ boolean disabled = disabledIntent != null;
boolean changed = false;
- if (mDisabledByAppOps != disabled) {
- mDisabledByAppOps = disabled;
+ if (mDisabledByEcm != disabled) {
+ mDisabledByEcmIntent = disabledIntent;
+ mDisabledByEcm = disabled;
changed = true;
updateDisabledState();
}
@@ -226,8 +257,8 @@
return mDisabledByAdmin;
}
- public boolean isDisabledByAppOps() {
- return mDisabledByAppOps;
+ public boolean isDisabledByEcm() {
+ return mDisabledByEcm;
}
public void updatePackageDetails(String packageName, int uid) {
@@ -236,13 +267,31 @@
}
private void updateDisabledState() {
+ boolean isEnabled = !(mDisabledByAdmin || mDisabledByEcm);
if (!(mPreference instanceof RestrictedTopLevelPreference)) {
- mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+ mPreference.setEnabled(isEnabled);
}
if (mPreference instanceof PrimarySwitchPreference) {
- ((PrimarySwitchPreference) mPreference)
- .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+ ((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled);
}
}
+
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
+ public boolean setDisabledByAppOps(boolean disabled) {
+ boolean changed = false;
+ if (mDisabledByEcm != disabled) {
+ mDisabledByEcm = disabled;
+ changed = true;
+ updateDisabledState();
+ }
+
+ return changed;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 60321eb..3b8f665 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -197,6 +197,17 @@
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param restriction The key identifying the setting
+ * @param packageName the package to check the restriction for
+ * @param uid the uid of the package
+ */
+ public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
+ mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ }
+
@Override
public void setEnabled(boolean enabled) {
boolean changed = false;
@@ -204,8 +215,8 @@
mHelper.setDisabledByAdmin(null);
changed = true;
}
- if (enabled && isDisabledByAppOps()) {
- mHelper.setDisabledByAppOps(false);
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
changed = true;
}
if (!changed) {
@@ -223,25 +234,50 @@
return mHelper.isDisabledByAdmin();
}
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
private void setDisabledByAppOps(boolean disabled) {
if (mHelper.setDisabledByAppOps(disabled)) {
notifyChanged();
}
}
- public boolean isDisabledByAppOps() {
- return mHelper.isDisabledByAppOps();
- }
-
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
public int getUid() {
return mHelper != null ? mHelper.uid : Process.INVALID_UID;
}
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
public String getPackageName() {
return mHelper != null ? mHelper.packageName : null;
}
- /** Updates enabled state based on associated package. */
+ /**
+ * Updates enabled state based on associated package
+ *
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
+ @Deprecated
public void updateState(
@NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {
mHelper.updatePackageDetails(packageName, uid);
@@ -258,7 +294,7 @@
setEnabled(false);
} else if (isEnabled) {
setEnabled(true);
- } else if (appOpsAllowed && isDisabledByAppOps()) {
+ } else if (appOpsAllowed && isDisabledByEcm()) {
setEnabled(true);
} else if (!appOpsAllowed){
setDisabledByAppOps(true);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 460d9f0..ec456e0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -106,6 +106,9 @@
Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
+ // ACCESSIBILITY_QS_TARGETS needs to be restored after ENABLED_ACCESSIBILITY_SERVICES
+ // but before QS_TILES
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS,
Settings.Secure.QS_TILES,
Settings.Secure.QS_AUTO_ADDED_TILES,
Settings.Secure.CONTROLS_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 6c48110e..5ad14ce 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -320,6 +320,9 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ACCESSIBILITY_QS_TARGETS,
+ ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a978889..5afcd5d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,9 @@
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
SecureSettingsProto.Accessibility.BUTTON_TARGETS);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+ SecureSettingsProto.Accessibility.QS_TARGETS);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d12d9d6..bacab0f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -879,6 +879,9 @@
<uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" />
+ <!-- Permissions required for CTS test - CtsAccessibilityServiceTestCases-->
+ <uses-permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7061e2c..f10ac1b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -204,6 +204,7 @@
"lottie",
"LowLightDreamLib",
"motion_tool_lib",
+ "notification_flags_lib",
],
libs: [
"keepanno-annotations",
@@ -328,6 +329,7 @@
"androidx.compose.ui_ui",
"flag-junit",
"platform-test-annotations",
+ "notification_flags_lib",
],
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index f7d9056c3..9c46ebdc 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -21,7 +21,6 @@
import android.app.PendingIntent
import android.app.TaskInfo
import android.graphics.Matrix
-import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.os.Build
@@ -37,7 +36,6 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
import androidx.annotation.AnyThread
import androidx.annotation.BinderThread
@@ -93,7 +91,7 @@
val INTERPOLATORS =
LaunchAnimator.Interpolators(
positionInterpolator = Interpolators.EMPHASIZED,
- positionXInterpolator = createPositionXInterpolator(),
+ positionXInterpolator = Interpolators.EMPHASIZED_COMPLEMENT,
contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN,
contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f)
)
@@ -121,16 +119,6 @@
* cancelled by WM.
*/
private const val LONG_LAUNCH_TIMEOUT = 5_000L
-
- private fun createPositionXInterpolator(): Interpolator {
- val path =
- Path().apply {
- moveTo(0f, 0f)
- cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f)
- cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f)
- }
- return PathInterpolator(path)
- }
}
/**
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 47f5663..1d7c7d63 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -33,7 +33,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
@@ -311,15 +310,21 @@
Row(
modifier =
- modifier.pointerInput(Unit) {
- detectTapGestures(
- onDoubleTap = { offset ->
- // Depending on where the user double tapped, switch the elements such that
- // the endContent is closer to the side that was double tapped.
- setSwapped(offset.x < size.width / 2)
- }
- )
- },
+ modifier
+ .pointerInput(Unit) {
+ detectTapGestures(
+ onDoubleTap = { offset ->
+ // Depending on where the user double tapped, switch the elements such
+ // that the non-swapped element is closer to the side that was double
+ // tapped.
+ setSwapped(offset.x < size.width / 2)
+ }
+ )
+ }
+ .padding(
+ top = if (isHeightExpanded) 128.dp else 96.dp,
+ bottom = if (isHeightExpanded) 128.dp else 48.dp,
+ ),
) {
val animatedOffset by
animateFloatAsState(
@@ -359,20 +364,11 @@
UserSwitcher(
viewModel = viewModel,
- modifier = Modifier.weight(1f).align(Alignment.CenterVertically).swappable(),
+ modifier = Modifier.weight(1f).swappable(),
)
FoldAware(
- modifier =
- Modifier.weight(1f)
- .padding(
- if (isHeightExpanded) {
- PaddingValues(vertical = 128.dp)
- } else {
- PaddingValues(top = 94.dp, bottom = 48.dp)
- }
- )
- .swappable(inversed = true),
+ modifier = Modifier.weight(1f).swappable(inversed = true),
viewModel = viewModel,
aboveFold = {
Column(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 185a06c..d83f3aa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -14,6 +14,7 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -28,15 +29,20 @@
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.FixedSizeEdgeDetector
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transform
object Communal {
@@ -60,7 +66,7 @@
* This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture
* handling and transitions before the full Flexiglass layout is ready.
*/
-@OptIn(ExperimentalComposeUiApi::class)
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class)
@Composable
fun CommunalContainer(
modifier: Modifier = Modifier,
@@ -81,6 +87,15 @@
return
}
+ // This effect exposes the SceneTransitionLayout's observable transition state to the rest of
+ // the system, and unsets it when the view is disposed to avoid a memory leak.
+ DisposableEffect(viewModel, sceneTransitionLayoutState) {
+ viewModel.setTransitionState(
+ sceneTransitionLayoutState.observableTransitionState().map { it.toModel() }
+ )
+ onDispose { viewModel.setTransitionState(null) }
+ }
+
Box(modifier = modifier.fillMaxSize()) {
SceneTransitionLayout(
modifier = Modifier.fillMaxSize(),
@@ -171,18 +186,40 @@
Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
}
-// TODO(b/293899074): Remove these conversions once Compose can be used throughout SysUI.
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
object TransitionSceneKey {
val Blank = CommunalSceneKey.Blank.toTransitionSceneKey()
val Communal = CommunalSceneKey.Communal.toTransitionSceneKey()
}
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
+fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
+ return this.identity as CommunalSceneKey
+}
+
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
fun CommunalSceneKey.toTransitionSceneKey(): SceneKey {
return SceneKey(name = toString(), identity = this)
}
-fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
- return this.identity as CommunalSceneKey
+/**
+ * Converts between the [SceneTransitionLayout] state class and our forked data class that can be
+ * used throughout SysUI.
+ */
+// TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI.
+fun ObservableTransitionState.toModel(): ObservableCommunalTransitionState {
+ return when (this) {
+ is ObservableTransitionState.Idle ->
+ ObservableCommunalTransitionState.Idle(scene.toCommunalSceneKey())
+ is ObservableTransitionState.Transition ->
+ ObservableCommunalTransitionState.Transition(
+ fromScene = fromScene.toCommunalSceneKey(),
+ toScene = toScene.toCommunalSceneKey(),
+ progress = progress,
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
+ )
+ }
}
object ContainerDimensions {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 2b11952..31604a6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -213,22 +213,11 @@
override fun onDetach() {
super.onDetach()
removeNodeFromSceneValues()
+ maybePruneMaps(layoutImpl, element, sceneValues)
}
private fun removeNodeFromSceneValues() {
sceneValues.nodes.remove(this)
-
- // If element is not composed from this scene anymore, remove the scene values. This works
- // because [onAttach] is called before [onDetach], so if an element is moved from the UI
- // tree we will first add the new code location then remove the old one.
- if (sceneValues.nodes.isEmpty()) {
- element.sceneValues.remove(sceneValues.scene)
- }
-
- // If the element is not composed in any scene, remove it from the elements map.
- if (element.sceneValues.isEmpty()) {
- layoutImpl.elements.remove(element.key)
- }
}
fun update(
@@ -237,12 +226,16 @@
element: Element,
sceneValues: Element.TargetValues,
) {
+ check(layoutImpl == this.layoutImpl && scene == this.scene)
removeNodeFromSceneValues()
- this.layoutImpl = layoutImpl
- this.scene = scene
+
+ val prevElement = this.element
+ val prevSceneValues = this.sceneValues
this.element = element
this.sceneValues = sceneValues
+
addNodeToSceneValues()
+ maybePruneMaps(layoutImpl, prevElement, prevSceneValues)
}
override fun ContentDrawScope.draw() {
@@ -261,6 +254,28 @@
}
}
}
+
+ companion object {
+ private fun maybePruneMaps(
+ layoutImpl: SceneTransitionLayoutImpl,
+ element: Element,
+ sceneValues: Element.TargetValues,
+ ) {
+ // If element is not composed from this scene anymore, remove the scene values. This
+ // works because [onAttach] is called before [onDetach], so if an element is moved from
+ // the UI tree we will first add the new code location then remove the old one.
+ if (
+ sceneValues.nodes.isEmpty() && element.sceneValues[sceneValues.scene] == sceneValues
+ ) {
+ element.sceneValues.remove(sceneValues.scene)
+
+ // If the element is not composed in any scene, remove it from the elements map.
+ if (element.sceneValues.isEmpty() && layoutImpl.elements[element.key] == element) {
+ layoutImpl.elements.remove(element.key)
+ }
+ }
+ }
+ }
}
private fun shouldDrawElement(
@@ -615,7 +630,9 @@
val toValues = element.sceneValues[toScene]
if (fromValues == null && toValues == null) {
- error("This should not happen, element $element is neither in $fromScene or $toScene")
+ // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
+ // run anymore.
+ return lastValue()
}
// The element is shared: interpolate between the value in fromScene and the value in toScene.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index ded6cc1..32025b4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -73,37 +73,37 @@
internal fun Modifier.nestedScrollToScene(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) =
this then
NestedScrollToSceneElement(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
private data class NestedScrollToSceneElement(
private val layoutImpl: SceneTransitionLayoutImpl,
private val orientation: Orientation,
- private val startBehavior: NestedScrollBehavior,
- private val endBehavior: NestedScrollBehavior,
+ private val topOrLeftBehavior: NestedScrollBehavior,
+ private val bottomOrRightBehavior: NestedScrollBehavior,
) : ModifierNodeElement<NestedScrollToSceneNode>() {
override fun create() =
NestedScrollToSceneNode(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
override fun update(node: NestedScrollToSceneNode) {
node.update(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
}
@@ -111,23 +111,23 @@
name = "nestedScrollToScene"
properties["layoutImpl"] = layoutImpl
properties["orientation"] = orientation
- properties["startBehavior"] = startBehavior
- properties["endBehavior"] = endBehavior
+ properties["topOrLeftBehavior"] = topOrLeftBehavior
+ properties["bottomOrRightBehavior"] = bottomOrRightBehavior
}
}
private class NestedScrollToSceneNode(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) : DelegatingNode() {
private var priorityNestedScrollConnection: PriorityNestedScrollConnection =
scenePriorityNestedScrollConnection(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
private var nestedScrollNode: DelegatableNode =
@@ -148,8 +148,8 @@
fun update(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) {
// Clean up the old nested scroll connection
priorityNestedScrollConnection.reset()
@@ -160,8 +160,8 @@
scenePriorityNestedScrollConnection(
layoutImpl = layoutImpl,
orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
nestedScrollNode =
nestedScrollModifierNode(
@@ -175,12 +175,12 @@
private fun scenePriorityNestedScrollConnection(
layoutImpl: SceneTransitionLayoutImpl,
orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ topOrLeftBehavior: NestedScrollBehavior,
+ bottomOrRightBehavior: NestedScrollBehavior,
) =
SceneNestedScrollHandler(
gestureHandler = layoutImpl.gestureHandler(orientation = orientation),
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ topOrLeftBehavior = topOrLeftBehavior,
+ bottomOrRightBehavior = bottomOrRightBehavior,
)
.connection
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index f5561cb..6a7a3a0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -86,16 +86,26 @@
return element(layoutImpl, scene, key)
}
- override fun Modifier.nestedScrollToScene(
- orientation: Orientation,
- startBehavior: NestedScrollBehavior,
- endBehavior: NestedScrollBehavior,
+ override fun Modifier.horizontalNestedScrollToScene(
+ leftBehavior: NestedScrollBehavior,
+ rightBehavior: NestedScrollBehavior,
): Modifier =
nestedScrollToScene(
layoutImpl = layoutImpl,
- orientation = orientation,
- startBehavior = startBehavior,
- endBehavior = endBehavior,
+ orientation = Orientation.Horizontal,
+ topOrLeftBehavior = leftBehavior,
+ bottomOrRightBehavior = rightBehavior,
+ )
+
+ override fun Modifier.verticalNestedScrollToScene(
+ topBehavior: NestedScrollBehavior,
+ bottomBehavior: NestedScrollBehavior
+ ): Modifier =
+ nestedScrollToScene(
+ layoutImpl = layoutImpl,
+ orientation = Orientation.Vertical,
+ topOrLeftBehavior = topBehavior,
+ bottomOrRightBehavior = bottomBehavior,
)
@Composable
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 212c9eb6..03f37d0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -494,9 +494,9 @@
}
internal class SceneNestedScrollHandler(
- private val gestureHandler: SceneGestureHandler,
- private val startBehavior: NestedScrollBehavior,
- private val endBehavior: NestedScrollBehavior,
+ private val gestureHandler: SceneGestureHandler,
+ private val topOrLeftBehavior: NestedScrollBehavior,
+ private val bottomOrRightBehavior: NestedScrollBehavior,
) : NestedScrollHandler {
override val connection: PriorityNestedScrollConnection = nestedScrollConnection()
@@ -565,8 +565,8 @@
canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
val behavior: NestedScrollBehavior =
when {
- offsetAvailable > 0f -> startBehavior
- offsetAvailable < 0f -> endBehavior
+ offsetAvailable > 0f -> topOrLeftBehavior
+ offsetAvailable < 0f -> bottomOrRightBehavior
else -> return@PriorityNestedScrollConnection false
}
@@ -594,8 +594,8 @@
canStartPostFling = { velocityAvailable ->
val behavior: NestedScrollBehavior =
when {
- velocityAvailable > 0f -> startBehavior
- velocityAvailable < 0f -> endBehavior
+ velocityAvailable > 0f -> topOrLeftBehavior
+ velocityAvailable < 0f -> bottomOrRightBehavior
else -> return@PriorityNestedScrollConnection false
}
@@ -604,6 +604,7 @@
behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
},
canContinueScroll = { true },
+ canScrollOnFling = false,
onStart = { offsetAvailable ->
gestureHandler.gestureWithPriority = this
gestureHandler.onDragStarted(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index afa184b..239971f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -126,14 +126,24 @@
* Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable
* component.
*
- * @param orientation is used to determine if we handle top/bottom or left/right events.
- * @param startBehavior when we should perform the overscroll animation at the top/left.
- * @param endBehavior when we should perform the overscroll animation at the bottom/right.
+ * @param leftBehavior when we should perform the overscroll animation at the left.
+ * @param rightBehavior when we should perform the overscroll animation at the right.
*/
- fun Modifier.nestedScrollToScene(
- orientation: Orientation,
- startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
- endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ fun Modifier.horizontalNestedScrollToScene(
+ leftBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ rightBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ ): Modifier
+
+ /**
+ * Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable
+ * component.
+ *
+ * @param topBehavior when we should perform the overscroll animation at the top.
+ * @param bottomBehavior when we should perform the overscroll animation at the bottom.
+ */
+ fun Modifier.verticalNestedScrollToScene(
+ topBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
+ bottomBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview,
): Modifier
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index f820074..dfa2a9a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -226,12 +226,17 @@
)
/**
- * Scale the element(s) matching [matcher] so that it grows/shrinks to the same size as [anchor]
- * .
+ * Scale the element(s) matching [matcher] so that it grows/shrinks to the same size as
+ * [anchor].
*
* Note: This currently only works if [anchor] is a shared element of this transition.
*/
- fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey)
+ fun anchoredSize(
+ matcher: ElementMatcher,
+ anchor: ElementKey,
+ anchorWidth: Boolean = true,
+ anchorHeight: Boolean = true,
+ )
}
/** The edge of a [SceneTransitionLayout]. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 8c0a5a3..8f4a36e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -178,7 +178,12 @@
transformation(DrawScale(matcher, scaleX, scaleY, pivot))
}
- override fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey) {
- transformation(AnchoredSize(matcher, anchor))
+ override fun anchoredSize(
+ matcher: ElementMatcher,
+ anchor: ElementKey,
+ anchorWidth: Boolean,
+ anchorHeight: Boolean,
+ ) {
+ transformation(AnchoredSize(matcher, anchor, anchorWidth, anchorHeight))
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 95385d5..40c814e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -29,6 +29,8 @@
internal class AnchoredSize(
override val matcher: ElementMatcher,
private val anchor: ElementKey,
+ private val anchorWidth: Boolean,
+ private val anchorHeight: Boolean,
) : PropertyTransformation<IntSize> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
@@ -41,7 +43,10 @@
fun anchorSizeIn(scene: SceneKey): IntSize {
val size = layoutImpl.elements[anchor]?.sceneValues?.get(scene)?.targetSize
return if (size != null && size != Element.SizeUnspecified) {
- size
+ IntSize(
+ width = if (anchorWidth) size.width else value.width,
+ height = if (anchorHeight) size.height else value.height,
+ )
} else {
value
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index cea8d9a..2c96d0e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -16,9 +16,8 @@
package com.android.compose.nestedscroll
-import androidx.compose.ui.geometry.Offset
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
/**
* A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -34,59 +33,44 @@
*
* @sample com.android.compose.animation.scene.demo.Shade
*/
-class LargeTopAppBarNestedScrollConnection(
- private val height: () -> Float,
- private val onChangeHeight: (Float) -> Unit,
- private val minHeight: Float,
- private val maxHeight: Float,
-) : NestedScrollConnection {
-
- constructor(
- height: () -> Float,
- onHeightChanged: (Float) -> Unit,
- heightRange: ClosedFloatingPointRange<Float>,
- ) : this(
- height = height,
- onChangeHeight = onHeightChanged,
- minHeight = heightRange.start,
- maxHeight = heightRange.endInclusive,
+fun LargeTopAppBarNestedScrollConnection(
+ height: () -> Float,
+ onHeightChanged: (Float) -> Unit,
+ heightRange: ClosedFloatingPointRange<Float>,
+): PriorityNestedScrollConnection {
+ val minHeight = heightRange.start
+ val maxHeight = heightRange.endInclusive
+ return PriorityNestedScrollConnection(
+ orientation = Orientation.Vertical,
+ // When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will
+ // expand. Then, you can then scroll down the content.
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight
+ },
+ // When swiping down, the content will scroll up until it reaches the top. Then, the
+ // LargeTopAppBar will expand until it reaches its [maxHeight].
+ canStartPostScroll = { offsetAvailable, _ -> offsetAvailable > 0 && height() < maxHeight },
+ canStartPostFling = { false },
+ canContinueScroll = {
+ val currentHeight = height()
+ minHeight < currentHeight && currentHeight < maxHeight
+ },
+ canScrollOnFling = true,
+ onStart = { /* do nothing */},
+ onScroll = { offsetAvailable ->
+ val currentHeight = height()
+ val amountConsumed =
+ if (offsetAvailable > 0) {
+ val amountLeft = maxHeight - currentHeight
+ offsetAvailable.coerceAtMost(amountLeft)
+ } else {
+ val amountLeft = minHeight - currentHeight
+ offsetAvailable.coerceAtLeast(amountLeft)
+ }
+ onHeightChanged(currentHeight + amountConsumed)
+ amountConsumed
+ },
+ // Don't consume the velocity on pre/post fling
+ onStop = { 0f },
)
-
- /**
- * When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will expand.
- * Then, you can then scroll down the content.
- */
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- val y = available.y
- val currentHeight = height()
- if (y >= 0 || currentHeight <= minHeight) {
- return Offset.Zero
- }
-
- val amountLeft = minHeight - currentHeight
- val amountConsumed = y.coerceAtLeast(amountLeft)
- onChangeHeight(currentHeight + amountConsumed)
- return Offset(0f, amountConsumed)
- }
-
- /**
- * When swiping down, the content will scroll up until it reaches the top. Then, the
- * LargeTopAppBar will expand until it reaches its [maxHeight].
- */
- override fun onPostScroll(
- consumed: Offset,
- available: Offset,
- source: NestedScrollSource
- ): Offset {
- val y = available.y
- val currentHeight = height()
- if (y <= 0 || currentHeight >= maxHeight) {
- return Offset.Zero
- }
-
- val amountLeft = maxHeight - currentHeight
- val amountConsumed = y.coerceAtMost(amountLeft)
- onChangeHeight(currentHeight + amountConsumed)
- return Offset(0f, amountConsumed)
- }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index c49a2b8..2841bcf 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -38,6 +38,7 @@
private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
private val canContinueScroll: () -> Boolean,
+ private val canScrollOnFling: Boolean,
private val onStart: (offsetAvailable: Offset) -> Unit,
private val onScroll: (offsetAvailable: Offset) -> Offset,
private val onStop: (velocityAvailable: Velocity) -> Velocity,
@@ -59,7 +60,7 @@
if (
isPriorityMode ||
- source == NestedScrollSource.Fling ||
+ (source == NestedScrollSource.Fling && !canScrollOnFling) ||
!canStartPostScroll(available, offsetBeforeStart)
) {
// The priority mode cannot start so we won't consume the available offset.
@@ -71,7 +72,7 @@
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (!isPriorityMode) {
- if (source != NestedScrollSource.Fling) {
+ if (source != NestedScrollSource.Fling || canScrollOnFling) {
if (canStartPreScroll(available, offsetScrolledBeforePriorityMode)) {
return onPriorityStart(available)
}
@@ -98,12 +99,20 @@
}
override suspend fun onPreFling(available: Velocity): Velocity {
+ if (isPriorityMode && canScrollOnFling) {
+ // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ return Velocity.Zero
+ }
// Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed
// of the fling gesture.
return onPriorityStop(velocity = available)
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ if (isPriorityMode) {
+ return onPriorityStop(velocity = available)
+ }
+
if (!canStartPostFling(available)) {
return Velocity.Zero
}
@@ -156,6 +165,7 @@
canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
canStartPostFling: (velocityAvailable: Float) -> Boolean,
canContinueScroll: () -> Boolean,
+ canScrollOnFling: Boolean,
onStart: (offsetAvailable: Float) -> Unit,
onScroll: (offsetAvailable: Float) -> Float,
onStop: (velocityAvailable: Float) -> Float,
@@ -172,6 +182,7 @@
canStartPostFling(velocityAvailable.toFloat())
},
canContinueScroll = canContinueScroll,
+ canScrollOnFling = canScrollOnFling,
onStart = { offsetAvailable -> onStart(offsetAvailable.toFloat()) },
onScroll = { offsetAvailable: Offset ->
onScroll(offsetAvailable.toFloat()).toOffset()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index ce3e1db..439dc00 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -17,16 +17,21 @@
package com.android.compose.animation.scene
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -36,6 +41,9 @@
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
@@ -430,6 +438,97 @@
}
@Test
+ @OptIn(ExperimentalFoundationApi::class)
+ fun elementModifierNodeIsRecycledInLazyLayouts() = runTest {
+ val nPages = 2
+ val pagerState = PagerState(currentPage = 0) { nPages }
+ var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
+
+ // This is how we scroll a pager inside a test, as explained in b/315457147#comment2.
+ lateinit var scrollScope: CoroutineScope
+ fun scrollToPage(page: Int) {
+ var animationFinished by mutableStateOf(false)
+ rule.runOnIdle {
+ scrollScope.launch {
+ pagerState.scrollToPage(page)
+ animationFinished = true
+ }
+ }
+ rule.waitUntil(timeoutMillis = 10_000) { animationFinished }
+ }
+
+ rule.setContent {
+ scrollScope = rememberCoroutineScope()
+
+ SceneTransitionLayoutForTesting(
+ currentScene = TestScenes.SceneA,
+ onChangeScene = {},
+ transitions = remember { transitions {} },
+ state = remember { SceneTransitionLayoutState(TestScenes.SceneA) },
+ edgeDetector = DefaultEdgeDetector,
+ modifier = Modifier,
+ transitionInterceptionThreshold = 0f,
+ onLayoutImpl = { nullableLayoutImpl = it },
+ ) {
+ scene(TestScenes.SceneA) {
+ // The pages are full-size and beyondBoundsPageCount is 0, so at rest only one
+ // page should be composed.
+ HorizontalPager(
+ pagerState,
+ beyondBoundsPageCount = 0,
+ ) { page ->
+ when (page) {
+ 0 -> Box(Modifier.element(TestElements.Foo).fillMaxSize())
+ 1 -> Box(Modifier.fillMaxSize())
+ else -> error("page $page < nPages $nPages")
+ }
+ }
+ }
+ }
+ }
+
+ assertThat(nullableLayoutImpl).isNotNull()
+ val layoutImpl = nullableLayoutImpl!!
+
+ // There is only Foo in the elements map.
+ assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
+ val element = layoutImpl.elements.getValue(TestElements.Foo)
+ val sceneValues = element.sceneValues
+ assertThat(sceneValues.keys).containsExactly(TestScenes.SceneA)
+
+ // Get the ElementModifier node that should be reused later on when coming back to this
+ // page.
+ val nodes = sceneValues.getValue(TestScenes.SceneA).nodes
+ assertThat(nodes).hasSize(1)
+ val node = nodes.single()
+
+ // Go to the second page.
+ scrollToPage(1)
+ rule.waitForIdle()
+
+ assertThat(nodes).isEmpty()
+ assertThat(sceneValues).isEmpty()
+ assertThat(layoutImpl.elements).isEmpty()
+
+ // Go back to the first page.
+ scrollToPage(0)
+ rule.waitForIdle()
+
+ assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
+ val newElement = layoutImpl.elements.getValue(TestElements.Foo)
+ val newSceneValues = newElement.sceneValues
+ assertThat(newElement).isNotEqualTo(element)
+ assertThat(newSceneValues).isNotEqualTo(sceneValues)
+ assertThat(newSceneValues.keys).containsExactly(TestScenes.SceneA)
+
+ // The ElementModifier node should be the same as before.
+ val newNodes = newSceneValues.getValue(TestScenes.SceneA).nodes
+ assertThat(newNodes).hasSize(1)
+ val newNode = newNodes.single()
+ assertThat(newNode).isSameInstanceAs(node)
+ }
+
+ @Test
fun existingElementsDontRecomposeWhenTransitionStateChanges() {
var fooCompositions = 0
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index aa942e0..34afc4c 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -117,8 +117,8 @@
fun nestedScrollConnection(nestedScrollBehavior: NestedScrollBehavior) =
SceneNestedScrollHandler(
gestureHandler = sceneGestureHandler,
- startBehavior = nestedScrollBehavior,
- endBehavior = nestedScrollBehavior,
+ topOrLeftBehavior = nestedScrollBehavior,
+ bottomOrRightBehavior = nestedScrollBehavior,
)
.connection
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
index 8ef6757..e555a01 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
@@ -85,4 +85,50 @@
after { onElement(TestElements.Bar).assertDoesNotExist() }
}
}
+
+ @Test
+ fun testAnchoredWidthOnly() {
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.size(100.dp, 100.dp).element(TestElements.Foo)) },
+ toSceneContent = {
+ Box(Modifier.size(50.dp, 50.dp).element(TestElements.Foo))
+ Box(Modifier.size(200.dp, 60.dp).element(TestElements.Bar))
+ },
+ transition = {
+ spec = tween(16 * 4, easing = LinearEasing)
+ anchoredSize(TestElements.Bar, TestElements.Foo, anchorHeight = false)
+ },
+ ) {
+ before { onElement(TestElements.Bar).assertDoesNotExist() }
+ at(0) { onElement(TestElements.Bar).assertSizeIsEqualTo(100.dp, 60.dp) }
+ at(16) { onElement(TestElements.Bar).assertSizeIsEqualTo(125.dp, 60.dp) }
+ at(32) { onElement(TestElements.Bar).assertSizeIsEqualTo(150.dp, 60.dp) }
+ at(48) { onElement(TestElements.Bar).assertSizeIsEqualTo(175.dp, 60.dp) }
+ at(64) { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 60.dp) }
+ after { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 60.dp) }
+ }
+ }
+
+ @Test
+ fun testAnchoredHeightOnly() {
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.size(100.dp, 100.dp).element(TestElements.Foo)) },
+ toSceneContent = {
+ Box(Modifier.size(50.dp, 50.dp).element(TestElements.Foo))
+ Box(Modifier.size(200.dp, 60.dp).element(TestElements.Bar))
+ },
+ transition = {
+ spec = tween(16 * 4, easing = LinearEasing)
+ anchoredSize(TestElements.Bar, TestElements.Foo, anchorWidth = false)
+ },
+ ) {
+ before { onElement(TestElements.Bar).assertDoesNotExist() }
+ at(0) { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 100.dp) }
+ at(16) { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 90.dp) }
+ at(32) { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 80.dp) }
+ at(48) { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 70.dp) }
+ at(64) { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 60.dp) }
+ after { onElement(TestElements.Bar).assertSizeIsEqualTo(200.dp, 60.dp) }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index 03d231a..e2974cd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -17,6 +17,7 @@
package com.android.compose.nestedscroll
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -36,6 +37,15 @@
heightRange = heightRange,
)
+ private fun NestedScrollConnection.scroll(
+ available: Offset,
+ consumedByScroll: Offset = Offset.Zero,
+ ) {
+ val consumedByPreScroll = onPreScroll(available = available, source = scrollSource)
+ val consumed = consumedByPreScroll + consumedByScroll
+ onPostScroll(consumed = consumed, available = available - consumed, source = scrollSource)
+ }
+
@Test
fun onScrollUp_consumeHeightFirst() {
val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
@@ -50,6 +60,41 @@
}
@Test
+ fun onScrollUpAfterContentScrolled_ignoreUpEvent() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+
+ // scroll down consumed by a child
+ scrollConnection.scroll(available = Offset(0f, 1f), consumedByScroll = Offset(0f, 1f))
+
+ val offsetConsumed =
+ scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
+
+ // It should ignore all onPreScroll events
+ assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(height).isEqualTo(1f)
+ }
+
+ @Test
+ fun onScrollUpAfterContentReturnedToZero_consumeHeight() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+
+ // scroll down consumed by a child
+ scrollConnection.scroll(available = Offset(0f, 1f), consumedByScroll = Offset(0f, 1f))
+
+ // scroll up consumed by a child, the child is in its original position
+ scrollConnection.scroll(available = Offset(0f, -1f), consumedByScroll = Offset(0f, -1f))
+
+ val offsetConsumed =
+ scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
+
+ // It should ignore all onPreScroll events
+ assertThat(offsetConsumed).isEqualTo(Offset(0f, -1f))
+ assertThat(height).isEqualTo(0f)
+ }
+
+ @Test
fun onScrollUp_consumeDownToMin() {
val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
height = 0f
@@ -110,6 +155,27 @@
}
@Test
+ fun onScrollDownAfterPostScroll_consumeHeightPreScroll() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+ scrollConnection.onPostScroll(
+ consumed = Offset.Zero,
+ available = Offset(x = 0f, y = 0.5f),
+ source = scrollSource
+ )
+
+ val offsetConsumed =
+ scrollConnection.onPreScroll(
+ available = Offset(x = 0f, y = 0.5f),
+ source = scrollSource
+ )
+ assertThat(offsetConsumed).isEqualTo(Offset(0f, 0.5f))
+
+ // It can increase by 1 (0.5f + 0.5f) the height
+ assertThat(height).isEqualTo(2f)
+ }
+
+ @Test
fun onScrollDown_consumeUpToMax() {
val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
height = 2f
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 122774b..8a9a92e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -46,6 +46,7 @@
canStartPostScroll = { _, _ -> canStartPostScroll },
canStartPostFling = { canStartPostFling },
canContinueScroll = { canContinueScroll },
+ canScrollOnFling = false,
onStart = { isStarted = true },
onScroll = {
lastScroll = it
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 7196de6..65176e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -20,6 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -29,6 +30,8 @@
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -40,29 +43,30 @@
class CommunalRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: CommunalRepositoryImpl
- private lateinit var testScope: TestScope
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
- private lateinit var sceneContainerFlags: FakeSceneContainerFlags
private lateinit var sceneContainerRepository: SceneContainerRepository
@Before
fun setUp() {
- testScope = TestScope()
-
val sceneTestUtils = SceneTestUtils(this)
- sceneContainerFlags = FakeSceneContainerFlags(enabled = false)
sceneContainerRepository = sceneTestUtils.fakeSceneContainerRepository()
featureFlagsClassic = FakeFeatureFlagsClassic()
featureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
- underTest =
- CommunalRepositoryImpl(
- featureFlagsClassic,
- sceneContainerFlags,
- sceneContainerRepository,
- )
+ underTest = createRepositoryImpl(false)
+ }
+
+ private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl {
+ return CommunalRepositoryImpl(
+ testScope.backgroundScope,
+ featureFlagsClassic,
+ FakeSceneContainerFlags(enabled = sceneContainerEnabled),
+ sceneContainerRepository,
+ )
}
@Test
@@ -86,13 +90,7 @@
@Test
fun isCommunalShowing_sceneContainerEnabled_onCommunalScene_true() =
testScope.runTest {
- sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
- underTest =
- CommunalRepositoryImpl(
- featureFlagsClassic,
- sceneContainerFlags,
- sceneContainerRepository,
- )
+ underTest = createRepositoryImpl(true)
sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Communal))
@@ -103,17 +101,49 @@
@Test
fun isCommunalShowing_sceneContainerEnabled_onLockscreenScene_false() =
testScope.runTest {
- sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
- underTest =
- CommunalRepositoryImpl(
- featureFlagsClassic,
- sceneContainerFlags,
- sceneContainerRepository,
- )
+ underTest = createRepositoryImpl(true)
sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Lockscreen))
val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
assertThat(isCommunalHubShowing).isFalse()
}
+
+ @Test
+ fun transitionState_idleByDefault() =
+ testScope.runTest {
+ val transitionState by collectLastValue(underTest.transitionState)
+ assertThat(transitionState)
+ .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
+ }
+
+ @Test
+ fun transitionState_setTransitionState_returnsNewValue() =
+ testScope.runTest {
+ val expectedSceneKey = CommunalSceneKey.Communal
+ underTest.setTransitionState(
+ flowOf(ObservableCommunalTransitionState.Idle(expectedSceneKey))
+ )
+
+ val transitionState by collectLastValue(underTest.transitionState)
+ assertThat(transitionState)
+ .isEqualTo(ObservableCommunalTransitionState.Idle(expectedSceneKey))
+ }
+
+ @Test
+ fun transitionState_setNullTransitionState_returnsDefaultValue() =
+ testScope.runTest {
+ // Set a value for the transition state flow.
+ underTest.setTransitionState(
+ flowOf(ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal))
+ )
+
+ // Set the transition state flow back to null.
+ underTest.setTransitionState(null)
+
+ // Flow returns default scene key.
+ val transitionState by collectLastValue(underTest.transitionState)
+ assertThat(transitionState)
+ .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS
new file mode 100644
index 0000000..cd04e82
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /packages/SystemUI/src/com/android/systemui/qs/OWNERS
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 2b744ac..00405d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.alarm.domain
import android.app.AlarmManager
+import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -40,7 +41,14 @@
private val kosmos = Kosmos()
private val alarmTileConfig = kosmos.qsAlarmTileConfig
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
- private val mapper by lazy { AlarmTileMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ AlarmTileMapper(
+ context.orCreateTestableResources
+ .apply { addOverride(R.drawable.ic_alarm, TestStubDrawable()) }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun notAlarmSet() {
@@ -100,7 +108,7 @@
): QSTileState {
val label = context.getString(R.string.status_bar_alarm)
return QSTileState(
- { Icon.Resource(R.drawable.ic_alarm, null) },
+ { Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null) },
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index 7b2ac90..b60f483 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,7 +36,17 @@
class FlashlightMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
- private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ FlashlightMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_flashlight_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_flashlight_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -56,20 +67,20 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
+ val expectedIcon =
+ Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
val actualIcon = tileState.icon()
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@Test
fun mapsDisabledDataToOffIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
+ val expectedIcon =
+ Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
val actualIcon = tileState.icon()
assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 8791877..ea74a4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.location.domain
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -36,7 +37,17 @@
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
- private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ LocationTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_location_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_location_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -56,20 +67,18 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
+ val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_on)!!, null)
val actualIcon = tileState.icon()
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
@Test
fun mapsDisabledDataToOffIconState() {
- val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null)
-
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
+ val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_off)!!, null)
val actualIcon = tileState.icon()
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index d182412..d162c77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.saver.domain
+import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -37,7 +38,17 @@
private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
- private val mapper by lazy { DataSaverTileMapper(context.orCreateTestableResources.resources) }
+ private val mapper by lazy {
+ DataSaverTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_data_saver_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_data_saver_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
+ }
@Test
fun activeStateMatchesEnabledModel() {
@@ -80,7 +91,7 @@
else context.resources.getStringArray(R.array.tile_states_saver)[0]
return QSTileState(
- { Icon.Resource(iconRes, null) },
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 87f5009..a977606 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.uimodenight.domain
import android.app.UiModeManager
+import android.graphics.drawable.TestStubDrawable
import android.text.TextUtils
import android.view.View
import android.widget.Switch
@@ -41,7 +42,15 @@
private val qsTileConfig = kosmos.qsUiModeNightTileConfig
private val mapper by lazy {
- UiModeNightTileMapper(context.orCreateTestableResources.resources)
+ UiModeNightTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_light_dark_theme_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_light_dark_theme_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme
+ )
}
private fun createUiNightModeTileState(
@@ -60,7 +69,7 @@
expandedAccessibilityClass: KClass<out View>? = Switch::class,
): QSTileState {
return QSTileState(
- { Icon.Resource(iconRes, null) },
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index fec96c6..317201d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -16,8 +16,6 @@
package com.android.systemui.shared.rotation;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-
import android.annotation.DimenRes;
import android.annotation.IdRes;
import android.annotation.LayoutRes;
@@ -89,8 +87,7 @@
@DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin,
@DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) {
- mContext = context.createWindowContext(context.getDisplay(), TYPE_NAVIGATION_BAR_PANEL,
- null);
+ mContext = context;
mWindowManager = mContext.getSystemService(WindowManager.class);
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(layout, null);
mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index bf68869..d8c1e41 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -103,8 +103,6 @@
public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
// The bubble stack is expanded AND the mange menu for bubbles is expanded on top of it.
public static final int SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1 << 23;
- // The current app is in immersive mode
- public static final int SYSUI_STATE_IMMERSIVE_MODE = 1 << 24;
// The voice interaction session window is showing
public static final int SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 25;
// Freeform windows are showing in desktop mode
@@ -162,7 +160,6 @@
SYSUI_STATE_DEVICE_DOZING,
SYSUI_STATE_BACK_DISABLED,
SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
- SYSUI_STATE_IMMERSIVE_MODE,
SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
SYSUI_STATE_DEVICE_DREAMING,
@@ -247,9 +244,6 @@
if ((flags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0) {
str.add("bubbles_mange_menu_expanded");
}
- if ((flags & SYSUI_STATE_IMMERSIVE_MODE) != 0) {
- str.add("immersive_mode");
- }
if ((flags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0) {
str.add("vis_win_showing");
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 3119b9e..1f4be40 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -18,18 +18,26 @@
import com.android.systemui.Flags.communalHub
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates the state of communal mode. */
interface CommunalRepository {
@@ -45,14 +53,26 @@
*/
val desiredScene: StateFlow<CommunalSceneKey>
+ /** Exposes the transition state of the communal [SceneTransitionLayout]. */
+ val transitionState: StateFlow<ObservableCommunalTransitionState>
+
/** Updates the requested scene. */
fun setDesiredScene(desiredScene: CommunalSceneKey)
+
+ /**
+ * Updates the transition state of the hub [SceneTransitionLayout].
+ *
+ * Note that you must call is with `null` when the UI is done or risk a memory leak.
+ */
+ fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?)
}
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class CommunalRepositoryImpl
@Inject
constructor(
+ @Background backgroundScope: CoroutineScope,
private val featureFlagsClassic: FeatureFlagsClassic,
sceneContainerFlags: SceneContainerFlags,
sceneContainerRepository: SceneContainerRepository,
@@ -61,13 +81,34 @@
get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
- MutableStateFlow(CommunalSceneKey.Blank)
+ MutableStateFlow(CommunalSceneKey.DEFAULT)
override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
+ private val defaultTransitionState =
+ ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)
+ private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null)
+ override val transitionState: StateFlow<ObservableCommunalTransitionState> =
+ _transitionState
+ .flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.Lazily,
+ initialValue = defaultTransitionState,
+ )
+
override fun setDesiredScene(desiredScene: CommunalSceneKey) {
_desiredScene.value = desiredScene
}
+ /**
+ * Updates the transition state of the hub [SceneTransitionLayout].
+ *
+ * Note that you must call is with `null` when the UI is done or risk a memory leak.
+ */
+ override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ _transitionState.value = transitionState
+ }
+
override val isCommunalHubShowing: Flow<Boolean> =
if (sceneContainerFlags.isEnabled()) {
sceneContainerRepository.desiredScene.map { scene -> scene.key == SceneKey.Communal }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index e630fd4..1a2a425 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -62,6 +63,19 @@
*/
val desiredScene: StateFlow<CommunalSceneKey> = communalRepository.desiredScene
+ /** Transition state of the hub mode. */
+ val transitionState: StateFlow<ObservableCommunalTransitionState> =
+ communalRepository.transitionState
+
+ /**
+ * Updates the transition state of the hub [SceneTransitionLayout].
+ *
+ * Note that you must call is with `null` when the UI is done or risk a memory leak.
+ */
+ fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ communalRepository.setTransitionState(transitionState)
+ }
+
/**
* Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
* [CommunalSceneKey.Communal].
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
index 2be909c..c68dd4f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
@@ -29,4 +29,8 @@
override fun toString(): String {
return loggingName
}
+
+ companion object {
+ val DEFAULT = Blank
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt
new file mode 100644
index 0000000..d834715
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/ObservableCommunalTransitionState.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.communal.shared.model
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * This is a fork of the `com.android.compose.animation.scene.ObservableTransitionState` class.
+ *
+ * TODO(b/315490861): remove this fork, once we can compile Compose into System UI.
+ */
+sealed class ObservableCommunalTransitionState {
+ /** No transition/animation is currently running. */
+ data class Idle(val scene: CommunalSceneKey) : ObservableCommunalTransitionState()
+
+ /** There is a transition animating between two scenes. */
+ data class Transition(
+ val fromScene: CommunalSceneKey,
+ val toScene: CommunalSceneKey,
+ val progress: Flow<Float>,
+
+ /**
+ * Whether the transition was originally triggered by user input rather than being
+ * programmatic. If this value is initially true, it will remain true until the transition
+ * fully completes, even if the user input that triggered the transition has ended. Any
+ * sub-transitions launched by this one will inherit this value. For example, if the user
+ * drags a pointer but does not exceed the threshold required to transition to another
+ * scene, this value will remain true after the pointer is no longer touching the screen and
+ * will be true in any transition created to animate back to the original position.
+ */
+ val isInitiatedByUserInput: Boolean,
+
+ /**
+ * Whether user input is currently driving the transition. For example, if a user is
+ * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+ * the transition completes/settles.
+ */
+ val isUserInputOngoing: Flow<Boolean>,
+ ) : ObservableCommunalTransitionState()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 333fc19..708f137 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -22,6 +22,7 @@
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.shade.ShadeViewController
import javax.inject.Provider
@@ -43,6 +44,15 @@
communalInteractor.onSceneChanged(scene)
}
+ /**
+ * Updates the transition state of the hub [SceneTransitionLayout].
+ *
+ * Note that you must call is with `null` when the UI is done or risk a memory leak.
+ */
+ fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ communalInteractor.setTransitionState(transitionState)
+ }
+
// TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block
// touches anymore.
/** Called when a touch is received outside the edge swipe area when hub mode is closed. */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index d5b95d67..5ec51f4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -16,6 +16,12 @@
package com.android.systemui.flags
+import com.android.server.notification.Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS
+import com.android.server.notification.Flags.FLAG_POLITE_NOTIFICATIONS
+import com.android.server.notification.Flags.FLAG_VIBRATE_WHILE_UNLOCKED
+import com.android.server.notification.Flags.crossAppPoliteNotifications
+import com.android.server.notification.Flags.politeNotifications
+import com.android.server.notification.Flags.vibrateWhileUnlocked
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.dagger.SysUISingleton
@@ -36,5 +42,14 @@
val keyguardBottomAreaRefactor = FlagToken(
FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor
+
+ val crossAppPoliteNotifToken =
+ FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications())
+ val politeNotifToken = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications())
+ crossAppPoliteNotifToken dependsOn politeNotifToken
+
+ val vibrateWhileUnlockedToken =
+ FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
+ vibrateWhileUnlockedToken dependsOn politeNotifToken
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b753ff7..b1d4587 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -111,7 +111,7 @@
// TODO(b/301955929)
@JvmField
val NOTIF_LS_BACKGROUND_THREAD =
- unreleasedFlag("notification_lockscreen_mgr_bg_thread", teamfood = true)
+ releasedFlag("notification_lockscreen_mgr_bg_thread")
// 200 - keyguard/lockscreen
// ** Flag retired **
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 9cdf857..992eeca 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -40,6 +40,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -76,6 +77,7 @@
import java.io.PrintWriter
import java.util.Locale
import java.util.TreeMap
+import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
@@ -102,6 +104,7 @@
private val activityStarter: ActivityStarter,
private val systemClock: SystemClock,
@Main executor: DelayableExecutor,
+ @Background private val bgExecutor: Executor,
private val mediaManager: MediaDataManager,
configurationController: ConfigurationController,
falsingManager: FalsingManager,
@@ -1030,7 +1033,7 @@
desiredHostState?.let {
if (this.desiredLocation != desiredLocation) {
// Only log an event when location changes
- logger.logCarouselPosition(desiredLocation)
+ bgExecutor.execute { logger.logCarouselPosition(desiredLocation) }
}
// This is a hosting view, let's remeasure our players
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 8d1ff98a..62c7343 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -31,7 +31,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -322,7 +321,6 @@
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
- .setFlag(SYSUI_STATE_IMMERSIVE_MODE, isImmersiveMode())
.commitUpdate(mDisplayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt
index fdc70a8..76ef8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt
@@ -278,7 +278,12 @@
d.setShowForAllUsers(true)
d.addOnDismissListener(onDialogDismissed)
if (view != null) {
- dialogLaunchAnimator.showFromView(d, view)
+ val controller = getPrivacyDialogController(view)
+ if (controller == null) {
+ d.show()
+ } else {
+ dialogLaunchAnimator.show(d, controller)
+ }
} else {
d.show()
}
@@ -291,6 +296,13 @@
}
}
+ private fun getPrivacyDialogController(source: View): DialogLaunchAnimator.Controller? {
+ val delegate = DialogLaunchAnimator.Controller.fromView(source) ?: return null
+ return object : DialogLaunchAnimator.Controller by delegate {
+ override fun shouldAnimateExit() = false
+ }
+ }
+
/** Dismisses the dialog */
fun dismissDialog() {
dialog?.dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/OWNERS b/packages/SystemUI/src/com/android/systemui/qs/OWNERS
new file mode 100644
index 0000000..45a4b50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/OWNERS
@@ -0,0 +1,10 @@
+set noparent
+
+# Bug component: 78010
+
+apotapov@google.com
+asc@google.com
+bhnm@google.com
+kozynski@google.com
+ostonge@google.com
+pixel@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
index 09d7a1f..17b78eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
@@ -24,7 +24,7 @@
* [QSTileInput.data]. It's guaranteed that [QSTileInput.userId] is the same as the id passed to
* [QSTileDataInteractor] to get [QSTileInput.data].
*
- * It's safe to run long running computations inside this function in this.
+ * It's safe to run long running computations inside this function.
*/
@WorkerThread suspend fun handleInput(input: QSTileInput<DATA_TYPE>)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index 4a34276..b325b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.di
+import android.content.Context
+import android.content.res.Resources.Theme
import com.android.systemui.qs.external.CustomTileStatePersister
import com.android.systemui.qs.external.CustomTileStatePersisterImpl
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
@@ -27,6 +29,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.Multibinds
/** Module listing subcomponents */
@@ -57,4 +60,9 @@
@Binds
fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister
+
+ companion object {
+
+ @Provides fun provideTilesTheme(context: Context): Theme = context.theme
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 2350b5d..9d214e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -59,12 +59,12 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wifitrackerlib.WifiEntry;
@@ -157,14 +157,6 @@
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
- protected boolean mIsSearchingHidden;
- protected final Runnable mHideProgressBarRunnable = () -> {
- setProgressBarVisible(false);
- };
- protected Runnable mHideSearchingRunnable = () -> {
- mIsSearchingHidden = true;
- mInternetDialogSubTitle.setText(getSubtitleText());
- };
@Inject
public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
@@ -285,8 +277,6 @@
if (DEBUG) {
Log.d(TAG, "onStop");
}
- mHandler.removeCallbacks(mHideProgressBarRunnable);
- mHandler.removeCallbacks(mHideSearchingRunnable);
mMobileNetworkLayout.setOnClickListener(null);
mConnectedWifListLayout.setOnClickListener(null);
if (mSecondaryMobileNetworkLayout != null) {
@@ -335,7 +325,6 @@
return;
}
- showProgressBar();
final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
final boolean isWifiEnabled = mInternetDialogController.isWifiEnabled();
final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
@@ -641,8 +630,7 @@
@Nullable
CharSequence getSubtitleText() {
- return mInternetDialogController.getSubtitleText(
- mIsProgressBarVisible && !mIsSearchingHidden);
+ return mInternetDialogController.getSubtitleText(mIsProgressBarVisible);
}
private Drawable getSignalStrengthDrawable(int subId) {
@@ -657,20 +645,6 @@
return mInternetDialogController.getMobileNetworkSummary(subId);
}
- protected void showProgressBar() {
- if (!mInternetDialogController.isWifiEnabled()
- || mInternetDialogController.isDeviceLocked()) {
- setProgressBarVisible(false);
- return;
- }
- setProgressBarVisible(true);
- if (mConnectedWifiEntry != null || mWifiEntriesCount > 0) {
- mHandler.postDelayed(mHideProgressBarRunnable, PROGRESS_DELAY_MS);
- } else if (!mIsSearchingHidden) {
- mHandler.postDelayed(mHideSearchingRunnable, PROGRESS_DELAY_MS);
- }
- }
-
private void setProgressBarVisible(boolean visible) {
if (mIsProgressBarVisible == visible) {
return;
@@ -823,6 +797,11 @@
}
@Override
+ public void onWifiScan(boolean isScan) {
+ setProgressBarVisible(isScan);
+ }
+
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f516f55..592cb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,7 +75,6 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -84,6 +83,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -1129,6 +1129,15 @@
public void onSettingsActivityTriggered(Intent settingsIntent) {
}
+ @Override
+ public void onWifiScan(boolean isScan) {
+ if (!isWifiEnabled() || isDeviceLocked()) {
+ mCallback.onWifiScan(false);
+ return;
+ }
+ mCallback.onWifiScan(isScan);
+ }
+
private class InternetTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
@@ -1372,6 +1381,8 @@
void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
@Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
+
+ void onWifiScan(boolean isScan);
}
void makeOverlayToast(int stringId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index cfb5442..9b8dba1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.airplane.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -27,18 +28,25 @@
import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<AirplaneModeTileModel> {
+class AirplaneModeMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ val theme: Theme,
+) : QSTileDataToStateMapper<AirplaneModeTileModel> {
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
val icon =
- Icon.Resource(
- if (data.isEnabled) {
- R.drawable.qs_airplane_icon_on
- } else {
- R.drawable.qs_airplane_icon_off
- },
+ Icon.Loaded(
+ resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_airplane_icon_on
+ } else {
+ R.drawable.qs_airplane_icon_off
+ },
+ theme,
+ ),
contentDescription = null
)
this.icon = { icon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index 6386577..e075e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.alarm.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
@@ -30,14 +31,18 @@
import javax.inject.Inject
/** Maps [AlarmTileModel] to [QSTileState]. */
-class AlarmTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<AlarmTileModel> {
+class AlarmTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<AlarmTileModel> {
companion object {
val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E hh:mm a")
val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E HH:mm")
}
override fun map(config: QSTileConfig, data: AlarmTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
when (data) {
is AlarmTileModel.NextAlarmSet -> {
activationState = QSTileState.ActivationState.ACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index 881a6bd..1b3b584 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -27,18 +28,25 @@
import javax.inject.Inject
/** Maps [FlashlightTileModel] to [QSTileState]. */
-class FlashlightMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<FlashlightTileModel> {
+class FlashlightMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<FlashlightTileModel> {
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
val icon =
- Icon.Resource(
- if (data.isEnabled) {
- R.drawable.qs_flashlight_icon_on
- } else {
- R.drawable.qs_flashlight_icon_off
- },
+ Icon.Loaded(
+ resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ },
+ theme,
+ ),
contentDescription = null
)
this.icon = { icon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index 7e7034d..fe5445d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.impl.location.domain
import android.content.res.Resources
+import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -27,18 +28,25 @@
import javax.inject.Inject
/** Maps [LocationTileModel] to [QSTileState]. */
-class LocationTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<LocationTileModel> {
+class LocationTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<LocationTileModel> {
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
val icon =
- Icon.Resource(
- if (data.isEnabled) {
- R.drawable.qs_location_icon_on
- } else {
- R.drawable.qs_location_icon_off
- },
+ Icon.Loaded(
+ resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ },
+ theme,
+ ),
contentDescription = null
)
this.icon = { icon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
index 25b0913..df25600 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -27,20 +27,28 @@
import javax.inject.Inject
/** Maps [DataSaverTileModel] to [QSTileState]. */
-class DataSaverTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<DataSaverTileModel> {
+class DataSaverTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<DataSaverTileModel> {
override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
with(data) {
+ val iconRes: Int
if (isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- icon = { Icon.Resource(R.drawable.qs_data_saver_icon_on, null) }
+ iconRes = R.drawable.qs_data_saver_icon_on
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- icon = { Icon.Resource(R.drawable.qs_data_saver_icon_off, null) }
+ iconRes = R.drawable.qs_data_saver_icon_off
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null)
+ icon = { loadedIcon }
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
index 3f30c75..ffef2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
@@ -18,6 +18,7 @@
import android.app.UiModeManager
import android.content.res.Resources
+import android.content.res.Resources.Theme
import android.text.TextUtils
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
@@ -31,15 +32,19 @@
import javax.inject.Inject
/** Maps [UiModeNightTileModel] to [QSTileState]. */
-class UiModeNightTileMapper @Inject constructor(@Main private val resources: Resources) :
- QSTileDataToStateMapper<UiModeNightTileModel> {
+class UiModeNightTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<UiModeNightTileModel> {
companion object {
val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a")
val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
}
override fun map(config: QSTileConfig, data: UiModeNightTileModel): QSTileState =
with(data) {
- QSTileState.build(resources, config.uiConfig) {
+ QSTileState.build(resources, theme, config.uiConfig) {
var shouldSetSecondaryLabel = false
if (isPowerSave) {
@@ -116,8 +121,9 @@
if (activationState == QSTileState.ActivationState.ACTIVE)
R.drawable.qs_light_dark_theme_icon_on
else R.drawable.qs_light_dark_theme_icon_off
- val iconResource = Icon.Resource(iconRes, null)
- icon = { iconResource }
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null)
+ icon = { loadedIcon }
supportedActions =
if (activationState == QSTileState.ActivationState.UNAVAILABLE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 23e0cb6..be1b740 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles.viewmodel
import android.content.res.Resources
+import android.content.res.Resources.Theme
import android.service.quicksettings.Tile
import android.view.View
import android.widget.Switch
@@ -47,14 +48,17 @@
fun build(
resources: Resources,
+ theme: Theme,
config: QSTileUIConfig,
build: Builder.() -> Unit
- ): QSTileState =
- build(
- { Icon.Resource(config.iconRes, null) },
+ ): QSTileState {
+ val iconDrawable = resources.getDrawable(config.iconRes, theme)
+ return build(
+ { Icon.Loaded(iconDrawable, null) },
resources.getString(config.labelRes),
build,
)
+ }
fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
Builder(icon, label).apply(build).build()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index d23c85a..cfbd015 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -18,16 +18,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Handler;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
import java.util.stream.Stream;
@@ -46,13 +46,12 @@
protected int mMinimumDisplayTime;
protected int mStickyForSomeTimeAutoDismissTime;
protected int mAutoDismissTime;
- @VisibleForTesting
- public Handler mHandler;
+ private DelayableExecutor mExecutor;
- public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler,
- SystemClock systemClock) {
+ public AlertingNotificationManager(HeadsUpManagerLogger logger,
+ SystemClock systemClock, @Main DelayableExecutor executor) {
mLogger = logger;
- mHandler = handler;
+ mExecutor = executor;
mSystemClock = systemClock;
}
@@ -264,6 +263,7 @@
public long mEarliestRemovalTime;
@Nullable protected Runnable mRemoveAlertRunnable;
+ @Nullable private Runnable mCancelRemoveAlertRunnable;
public void setEntry(@NonNull final NotificationEntry entry) {
setEntry(entry, () -> removeAlertEntry(entry.getKey()));
@@ -291,13 +291,15 @@
if (updatePostTime) {
mPostTime = Math.max(mPostTime, now);
}
- removeAutoRemovalCallbacks("updateEntry (will be rescheduled)");
- if (!isSticky()) {
- final long finishTime = calculateFinishTime();
- final long timeLeft = Math.max(finishTime - now, mMinimumDisplayTime);
- mHandler.postDelayed(mRemoveAlertRunnable, timeLeft);
+ if (isSticky()) {
+ removeAutoRemovalCallbacks("updateEntry (sticky)");
+ return;
}
+
+ final long finishTime = calculateFinishTime();
+ final long timeLeft = Math.max(finishTime - now, mMinimumDisplayTime);
+ scheduleAutoRemovalCallback(timeLeft, "updateEntry (not sticky)");
}
/**
@@ -340,21 +342,50 @@
* Clear any pending removal runnables.
*/
public void removeAutoRemovalCallbacks(@Nullable String reason) {
- if (mRemoveAlertRunnable != null) {
+ final boolean removed = removeAutoRemovalCallbackInternal();
+
+ if (removed) {
mLogger.logAutoRemoveCanceled(mEntry, reason);
- mHandler.removeCallbacks(mRemoveAlertRunnable);
}
}
+ private void scheduleAutoRemovalCallback(long delayMillis, @NonNull String reason) {
+ if (mRemoveAlertRunnable == null) {
+ Log.wtf(TAG, "scheduleAutoRemovalCallback with no callback set");
+ return;
+ }
+
+ final boolean removed = removeAutoRemovalCallbackInternal();
+
+ if (removed) {
+ mLogger.logAutoRemoveRescheduled(mEntry, delayMillis, reason);
+ } else {
+ mLogger.logAutoRemoveScheduled(mEntry, delayMillis, reason);
+ }
+
+
+ mCancelRemoveAlertRunnable = mExecutor.executeDelayed(mRemoveAlertRunnable,
+ delayMillis);
+ }
+
+ private boolean removeAutoRemovalCallbackInternal() {
+ final boolean scheduled = (mCancelRemoveAlertRunnable != null);
+
+ if (scheduled) {
+ mCancelRemoveAlertRunnable.run();
+ mCancelRemoveAlertRunnable = null;
+ }
+
+ return scheduled;
+ }
+
/**
* Remove the alert at the earliest allowed removal time.
*/
public void removeAsSoonAsPossible() {
if (mRemoveAlertRunnable != null) {
- removeAutoRemovalCallbacks("removeAsSoonAsPossible (will be rescheduled)");
-
final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime();
- mHandler.postDelayed(mRemoveAlertRunnable, timeLeft);
+ scheduleAutoRemovalCallback(timeLeft, "removeAsSoonAsPossible");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index e486457..05c3839 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -18,6 +18,7 @@
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
@@ -46,6 +47,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -155,8 +157,22 @@
if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- boolean changed = updateDpcSettings(getSendingUserId());
- if (mCurrentUserId == getSendingUserId()) {
+ boolean changed = false;
+ int sendingUserId = getSendingUserId();
+ if (sendingUserId == USER_ALL) {
+ // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
+ // the user before calling into DPM
+ sendingUserId = mCurrentUserId;
+ @SuppressLint("MissingPermission")
+ List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ changed |= updateDpcSettings(users.get(i).id);
+ }
+ } else {
+ changed |= updateDpcSettings(sendingUserId);
+ }
+
+ if (mCurrentUserId == sendingUserId) {
changed |= updateLockscreenNotificationSetting();
}
if (changed) {
@@ -374,13 +390,13 @@
mContext.getContentResolver().registerContentObserver(
SHOW_LOCKSCREEN, false,
mLockscreenSettingsObserver,
- UserHandle.USER_ALL);
+ USER_ALL);
mContext.getContentResolver().registerContentObserver(
SHOW_PRIVATE_LOCKSCREEN,
true,
mLockscreenSettingsObserver,
- UserHandle.USER_ALL);
+ USER_ALL);
if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
mContext.getContentResolver().registerContentObserver(
@@ -441,7 +457,7 @@
public boolean isCurrentProfile(int userId) {
synchronized (mLock) {
- return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
+ return userId == USER_ALL || mCurrentProfiles.get(userId) != null;
}
}
@@ -526,7 +542,7 @@
*/
public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- if (userHandle == UserHandle.USER_ALL) {
+ if (userHandle == USER_ALL) {
userHandle = mCurrentUserId;
}
if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
@@ -540,7 +556,7 @@
return mUsersUsersAllowingPrivateNotifications.get(userHandle)
&& mUsersDpcAllowingPrivateNotifications.get(userHandle);
} else {
- if (userHandle == UserHandle.USER_ALL) {
+ if (userHandle == USER_ALL) {
return true;
}
@@ -574,7 +590,7 @@
}
private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
- if (userHandle == UserHandle.USER_ALL) {
+ if (userHandle == USER_ALL) {
return true;
}
final int dpmFlags =
@@ -591,7 +607,7 @@
}
public boolean isLockscreenPublicMode(int userId) {
- if (userId == UserHandle.USER_ALL) {
+ if (userId == USER_ALL) {
return mLockscreenPublicMode.get(mCurrentUserId, false);
}
return mLockscreenPublicMode.get(userId, false);
@@ -610,7 +626,7 @@
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
// Unlike 'show private', settings does not show a copy of this setting for each
// profile, so it inherits from the parent user.
- if (userHandle == UserHandle.USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
+ if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
userHandle = mCurrentUserId;
}
if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
index 490994d..fc474d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
@@ -89,5 +89,12 @@
* "wifi_start_connect_ssid" set as an extra
*/
fun onSettingsActivityTriggered(settingsIntent: Intent?)
+
+ /**
+ * Called whenever a Wi-Fi scan is triggered.
+ *
+ * @param isScan Whether Wi-Fi scan is triggered or not.
+ */
+ fun onWifiScan(isScan: Boolean)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 91ca148..3a31851 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -213,6 +213,12 @@
}
}
+ private void fireWifiScanCallback(boolean isScan) {
+ for (AccessPointCallback callback : mCallbacks) {
+ callback.onWifiScan(isScan);
+ }
+ }
+
void dump(PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
ipw.println("AccessPointControllerImpl:");
@@ -240,6 +246,14 @@
}
@Override
+ public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int reason) {
+ onWifiEntriesChanged();
+ if (reason == WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) {
+ fireWifiScanCallback(false /* isScan */);
+ }
+ }
+
+ @Override
public void onNumSavedNetworksChanged() {
// Do nothing
}
@@ -249,6 +263,11 @@
// Do nothing
}
+ @Override
+ public void onScanRequested() {
+ fireWifiScanCallback(true /* isScan */);
+ }
+
private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() {
@Override
public void onConnectResult(int status) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 644c896..e66d9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -119,12 +120,13 @@
@Main Handler handler,
GlobalSettings globalSettings,
SystemClock systemClock,
+ @Main DelayableExecutor executor,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
ShadeInteractor shadeInteractor) {
- super(context, logger, handler, globalSettings, systemClock, accessibilityManagerWrapper,
- uiEventLogger);
+ super(context, logger, handler, globalSettings, systemClock, executor,
+ accessibilityManagerWrapper, uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
statusBarStateController.addCallback(mStatusBarStateListener);
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 daadedb..4999123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -36,6 +36,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
+import android.view.WindowInsetsController;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedDispatcher;
@@ -859,10 +860,19 @@
}
}
+ private void setRootViewAnimationDisabled(boolean disabled) {
+ ViewGroup windowRootView = mNotificationShadeWindowController.getWindowRootView();
+ if (windowRootView != null) {
+ WindowInsetsController insetsController = windowRootView.getWindowInsetsController();
+ if (insetsController != null) {
+ insetsController.setAnimationsDisabled(disabled);
+ }
+ }
+ }
+
@Override
public void onStartedWakingUp() {
- mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController()
- .setAnimationsDisabled(false);
+ setRootViewAnimationDisabled(false);
NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
navBarView.forEachView(view ->
@@ -875,8 +885,7 @@
@Override
public void onStartedGoingToSleep() {
- mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController()
- .setAnimationsDisabled(true);
+ setRootViewAnimationDisabled(true);
NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
navBarView.forEachView(view ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 8054b04..e971128 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.util.ListenerSet;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
@@ -87,9 +88,10 @@
@Main Handler handler,
GlobalSettings globalSettings,
SystemClock systemClock,
+ @Main DelayableExecutor executor,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger) {
- super(logger, handler, systemClock);
+ super(logger, systemClock, executor);
mContext = context;
mAccessibilityMgr = accessibilityManagerWrapper;
mUiEventLogger = uiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index ef07eed..f6154afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -66,6 +66,26 @@
})
}
+ fun logAutoRemoveScheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = entry.logKey
+ long1 = delayMillis
+ str2 = reason
+ }, {
+ "schedule auto remove of $str1 in $long1 ms reason: $str2"
+ })
+ }
+
+ fun logAutoRemoveRescheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = entry.logKey
+ long1 = delayMillis
+ str2 = reason
+ }, {
+ "reschedule auto remove of $str1 in $long1 ms reason: $str2"
+ })
+ }
+
fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
buffer.log(TAG, INFO, {
str1 = entry.logKey
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 3bfdb84..310e0b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -124,6 +125,7 @@
@Captor lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
private val clock = FakeSystemClock()
+ private lateinit var bgExecutor: FakeExecutor
private lateinit var mediaCarouselController: MediaCarouselController
@Before
@@ -131,6 +133,7 @@
MockitoAnnotations.initMocks(this)
context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK))
transitionRepository = FakeKeyguardTransitionRepository()
+ bgExecutor = FakeExecutor(clock)
mediaCarouselController =
MediaCarouselController(
context,
@@ -140,6 +143,7 @@
activityStarter,
clock,
executor,
+ bgExecutor,
mediaDataManager,
configurationController,
falsingManager,
@@ -458,6 +462,7 @@
mediaHostState,
animate = false
)
+ bgExecutor.runAllReady()
verify(logger).logCarouselPosition(LOCATION_QS)
}
@@ -468,6 +473,7 @@
mediaHostState,
animate = false
)
+ bgExecutor.runAllReady()
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS)
}
@@ -478,6 +484,7 @@
mediaHostState,
animate = false
)
+ bgExecutor.runAllReady()
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
@@ -488,6 +495,7 @@
mediaHostState,
animate = false
)
+ bgExecutor.runAllReady()
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
index 0a8c0ab..e4432f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
@@ -31,18 +31,20 @@
import android.permission.PermissionManager
import android.testing.AndroidTestingRunner
import android.view.View
+import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.animation.LaunchableView
import com.android.systemui.appops.AppOpsController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -56,12 +58,12 @@
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -83,60 +85,48 @@
private val TEST_INTENT = Intent("test_intent_action")
}
- @Mock
- private lateinit var dialog: PrivacyDialogV2
- @Mock
- private lateinit var permissionManager: PermissionManager
- @Mock
- private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var privacyItemController: PrivacyItemController
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var privacyLogger: PrivacyLogger
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var appOpsController: AppOpsController
+ @Mock private lateinit var dialog: PrivacyDialogV2
+ @Mock private lateinit var permissionManager: PermissionManager
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var privacyItemController: PrivacyItemController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var privacyLogger: PrivacyLogger
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var appOpsController: AppOpsController
@Captor
private lateinit var dialogDismissedCaptor: ArgumentCaptor<PrivacyDialogV2.OnDialogDismissed>
- @Captor
- private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
- @Captor
- private lateinit var intentCaptor: ArgumentCaptor<Intent>
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Captor private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
+ @Captor private lateinit var intentCaptor: ArgumentCaptor<Intent>
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
private val uiExecutor = FakeExecutor(FakeSystemClock())
private lateinit var controller: PrivacyDialogControllerV2
private var nextUid: Int = 0
- private val dialogProvider = object : PrivacyDialogControllerV2.DialogProvider {
- var list: List<PrivacyDialogV2.PrivacyElement>? = null
- var manageApp: ((String, Int, Intent) -> Unit)? = null
- var closeApp: ((String, Int) -> Unit)? = null
- var openPrivacyDashboard: (() -> Unit)? = null
+ private val dialogProvider =
+ object : PrivacyDialogControllerV2.DialogProvider {
+ var list: List<PrivacyDialogV2.PrivacyElement>? = null
+ var manageApp: ((String, Int, Intent) -> Unit)? = null
+ var closeApp: ((String, Int) -> Unit)? = null
+ var openPrivacyDashboard: (() -> Unit)? = null
- override fun makeDialog(
- context: Context,
- list: List<PrivacyDialogV2.PrivacyElement>,
- manageApp: (String, Int, Intent) -> Unit,
- closeApp: (String, Int) -> Unit,
- openPrivacyDashboard: () -> Unit
- ): PrivacyDialogV2 {
- this.list = list
- this.manageApp = manageApp
- this.closeApp = closeApp
- this.openPrivacyDashboard = openPrivacyDashboard
- return dialog
+ override fun makeDialog(
+ context: Context,
+ list: List<PrivacyDialogV2.PrivacyElement>,
+ manageApp: (String, Int, Intent) -> Unit,
+ closeApp: (String, Int) -> Unit,
+ openPrivacyDashboard: () -> Unit
+ ): PrivacyDialogV2 {
+ this.list = list
+ this.manageApp = manageApp
+ this.closeApp = closeApp
+ this.openPrivacyDashboard = openPrivacyDashboard
+ return dialog
+ }
}
- }
@Before
fun setUp() {
@@ -144,7 +134,8 @@
nextUid = 0
setUpDefaultMockResponses()
- controller = PrivacyDialogControllerV2(
+ controller =
+ PrivacyDialogControllerV2(
permissionManager,
packageManager,
privacyItemController,
@@ -158,7 +149,7 @@
uiEventLogger,
dialogLaunchAnimator,
dialogProvider
- )
+ )
}
@After
@@ -197,7 +188,7 @@
verify(packageManager, never()).getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
backgroundExecutor.runAllReady()
verify(packageManager, atLeastOnce())
- .getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
+ .getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
}
@Test
@@ -208,20 +199,25 @@
controller.showDialog(context)
exhaustExecutors()
- verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), anyBoolean())
+ verify(dialogLaunchAnimator, never()).show(any(), any(), anyBoolean())
verify(dialog).show()
}
@Test
fun testShowDialogShowsDialogWithView() {
- val view = View(context)
+ val parent = LinearLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
val usage = createMockPermGroupUsage()
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context, view)
exhaustExecutors()
- verify(dialogLaunchAnimator).showFromView(dialog, view)
+ verify(dialogLaunchAnimator).show(eq(dialog), any(), anyBoolean())
verify(dialog, never()).show()
}
@@ -276,7 +272,8 @@
@Test
fun testSingleElementInList() {
- val usage = createMockPermGroupUsage(
+ val usage =
+ createMockPermGroupUsage(
packageName = TEST_PACKAGE_NAME,
uid = generateUidForUser(USER_ID),
permissionGroupName = PERM_CAMERA,
@@ -285,7 +282,7 @@
isPhoneCall = false,
attributionTag = null,
proxyLabel = TEST_PROXY_LABEL
- )
+ )
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
@@ -304,33 +301,38 @@
assertThat(list.get(0).isPhoneCall).isFalse()
assertThat(list.get(0).isService).isFalse()
assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
}
}
private fun isIntentEqual(actual: Intent, expected: Intent): Boolean {
return actual.action == expected.action &&
- actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
+ actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
expected.getStringExtra(Intent.EXTRA_PACKAGE_NAME) &&
- actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
+ actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
expected.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle
}
@Test
fun testTwoElementsDifferentType_sorted() {
- val usage_camera = createMockPermGroupUsage(
+ val usage_camera =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_camera",
permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
+ )
+ val usage_microphone =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_microphone",
permissionGroupName = PERM_MICROPHONE
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_microphone, usage_camera)
- )
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_microphone, usage_camera))
controller.showDialog(context)
exhaustExecutors()
@@ -343,17 +345,12 @@
@Test
fun testTwoElementsSameType_oneActive() {
- val usage_active = createMockPermGroupUsage(
- packageName = "${TEST_PACKAGE_NAME}_active",
- isActive = true
- )
- val usage_recent = createMockPermGroupUsage(
- packageName = "${TEST_PACKAGE_NAME}_recent",
- isActive = false
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_recent, usage_active)
- )
+ val usage_active =
+ createMockPermGroupUsage(packageName = "${TEST_PACKAGE_NAME}_active", isActive = true)
+ val usage_recent =
+ createMockPermGroupUsage(packageName = "${TEST_PACKAGE_NAME}_recent", isActive = false)
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_recent, usage_active))
controller.showDialog(context)
exhaustExecutors()
@@ -364,19 +361,20 @@
@Test
fun testTwoElementsSameType_twoActive() {
- val usage_active = createMockPermGroupUsage(
+ val usage_active =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_active",
isActive = true,
lastAccessTimeMillis = 0L
- )
- val usage_active_moreRecent = createMockPermGroupUsage(
+ )
+ val usage_active_moreRecent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_active_recent",
isActive = true,
lastAccessTimeMillis = 1L
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_active, usage_active_moreRecent)
- )
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_active, usage_active_moreRecent))
controller.showDialog(context)
exhaustExecutors()
assertThat(dialogProvider.list).hasSize(2)
@@ -386,24 +384,26 @@
@Test
fun testManyElementsSameType_bothRecent() {
- val usage_recent = createMockPermGroupUsage(
+ val usage_recent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_recent",
isActive = false,
lastAccessTimeMillis = 0L
- )
- val usage_moreRecent = createMockPermGroupUsage(
+ )
+ val usage_moreRecent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_moreRecent",
isActive = false,
lastAccessTimeMillis = 1L
- )
- val usage_mostRecent = createMockPermGroupUsage(
+ )
+ val usage_mostRecent =
+ createMockPermGroupUsage(
packageName = "${TEST_PACKAGE_NAME}_mostRecent",
isActive = false,
lastAccessTimeMillis = 2L
- )
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_recent, usage_mostRecent, usage_moreRecent)
- )
+ )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_recent, usage_mostRecent, usage_moreRecent))
controller.showDialog(context)
exhaustExecutors()
@@ -414,19 +414,12 @@
@Test
fun testMicAndCameraDisabled() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
controller.showDialog(context)
@@ -438,45 +431,29 @@
@Test
fun testLocationDisabled() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.locationAvailable).thenReturn(false)
controller.showDialog(context)
exhaustExecutors()
assertThat(dialogProvider.list).hasSize(2)
- dialogProvider.list?.forEach {
- assertThat(it.type).isNotEqualTo(PrivacyType.TYPE_LOCATION)
- }
+ dialogProvider.list?.forEach { assertThat(it.type).isNotEqualTo(PrivacyType.TYPE_LOCATION) }
}
@Test
fun testAllIndicatorsAvailable() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
`when`(privacyItemController.locationAvailable).thenReturn(true)
@@ -488,19 +465,12 @@
@Test
fun testNoIndicatorsAvailable() {
- val usage_camera = createMockPermGroupUsage(
- permissionGroupName = PERM_CAMERA
- )
- val usage_microphone = createMockPermGroupUsage(
- permissionGroupName = PERM_MICROPHONE
- )
- val usage_location = createMockPermGroupUsage(
- permissionGroupName = PERM_LOCATION
- )
+ val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA)
+ val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE)
+ val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION)
- `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
- listOf(usage_camera, usage_location, usage_microphone)
- )
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_camera, usage_location, usage_microphone))
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
`when`(privacyItemController.locationAvailable).thenReturn(false)
@@ -512,11 +482,9 @@
@Test
fun testNotCurrentUser() {
- val usage_other = createMockPermGroupUsage(
- uid = generateUidForUser(ENT_USER_ID + 1)
- )
+ val usage_other = createMockPermGroupUsage(uid = generateUidForUser(ENT_USER_ID + 1))
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
- .thenReturn(listOf(usage_other))
+ .thenReturn(listOf(usage_other))
controller.showDialog(context)
exhaustExecutors()
@@ -559,9 +527,7 @@
// Calls happen in
val usage = createMockPermGroupUsage(uid = SYSTEM_UID, isPhoneCall = true)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(userTracker.userProfiles).thenReturn(listOf(
- UserInfo(ENT_USER_ID, "", 0)
- ))
+ `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(ENT_USER_ID, "", 0)))
controller.showDialog(context)
exhaustExecutors()
@@ -577,8 +543,12 @@
exhaustExecutors()
dialogProvider.manageApp?.invoke(TEST_PACKAGE_NAME, USER_ID, TEST_INTENT)
- verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
- USER_ID, TEST_PACKAGE_NAME)
+ verify(uiEventLogger)
+ .log(
+ PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
+ USER_ID,
+ TEST_PACKAGE_NAME
+ )
}
@Test
@@ -589,8 +559,12 @@
exhaustExecutors()
dialogProvider.closeApp?.invoke(TEST_PACKAGE_NAME, USER_ID)
- verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_CLOSE_APP,
- USER_ID, TEST_PACKAGE_NAME)
+ verify(uiEventLogger)
+ .log(
+ PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_CLOSE_APP,
+ USER_ID,
+ TEST_PACKAGE_NAME
+ )
}
@Test
@@ -629,9 +603,13 @@
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@@ -648,45 +626,58 @@
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(
- TEST_PACKAGE_NAME, ENT_USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(
+ TEST_PACKAGE_NAME,
+ ENT_USER_ID
+ )
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@Test
fun testDefaultIntentOnInvalidAttributionTag() {
- val usage = createMockPermGroupUsage(
+ val usage =
+ createMockPermGroupUsage(
attributionTag = "INVALID_ATTRIBUTION_TAG",
proxyLabel = TEST_PROXY_LABEL
- )
+ )
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@Test
fun testServiceIntentOnCorrectSubAttribution() {
- val usage = createMockPermGroupUsage(
+ val usage =
+ createMockPermGroupUsage(
attributionTag = TEST_ATTRIBUTION_TAG,
attributionLabel = "TEST_LABEL"
- )
+ )
val activityInfo = createMockActivityInfo()
val resolveInfo = createMockResolveInfo(activityInfo)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>()))
- .thenAnswer { resolveInfo }
+ `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer {
+ resolveInfo
+ }
controller.showDialog(context)
exhaustExecutors()
@@ -694,57 +685,61 @@
val navigationIntent = list.get(0).navigationIntent!!
assertThat(navigationIntent.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_USAGE)
assertThat(navigationIntent.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME))
- .isEqualTo(PERM_CAMERA)
+ .isEqualTo(PERM_CAMERA)
assertThat(navigationIntent.getStringArrayExtra(Intent.EXTRA_ATTRIBUTION_TAGS))
- .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString()))
+ .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString()))
assertThat(navigationIntent.getBooleanExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, false))
- .isTrue()
+ .isTrue()
assertThat(list.get(0).isService).isTrue()
}
}
@Test
fun testDefaultIntentOnMissingAttributionLabel() {
- val usage = createMockPermGroupUsage(
- attributionTag = TEST_ATTRIBUTION_TAG
- )
+ val usage = createMockPermGroupUsage(attributionTag = TEST_ATTRIBUTION_TAG)
val activityInfo = createMockActivityInfo()
val resolveInfo = createMockResolveInfo(activityInfo)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>()))
- .thenAnswer { resolveInfo }
+ `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer {
+ resolveInfo
+ }
controller.showDialog(context)
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@Test
fun testDefaultIntentOnIncorrectPermission() {
- val usage = createMockPermGroupUsage(
- attributionTag = TEST_ATTRIBUTION_TAG
- )
+ val usage = createMockPermGroupUsage(attributionTag = TEST_ATTRIBUTION_TAG)
- val activityInfo = createMockActivityInfo(
- permission = "INCORRECT_PERMISSION"
- )
+ val activityInfo = createMockActivityInfo(permission = "INCORRECT_PERMISSION")
val resolveInfo = createMockResolveInfo(activityInfo)
`when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
- `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>()))
- .thenAnswer { resolveInfo }
+ `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer {
+ resolveInfo
+ }
controller.showDialog(context)
exhaustExecutors()
dialogProvider.list?.let { list ->
- assertThat(isIntentEqual(list.get(0).navigationIntent!!,
- controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
- .isTrue()
+ assertThat(
+ isIntentEqual(
+ list.get(0).navigationIntent!!,
+ controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)
+ )
+ )
+ .isTrue()
assertThat(list.get(0).isService).isFalse()
}
}
@@ -758,15 +753,18 @@
`when`(appOpsController.isMicMuted).thenReturn(false)
`when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
- .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
+ .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
`when`(privacyItemController.locationAvailable).thenReturn(true)
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
- `when`(userTracker.userProfiles).thenReturn(listOf(
- UserInfo(USER_ID, "", 0),
- UserInfo(ENT_USER_ID, "", UserInfo.FLAG_MANAGED_PROFILE)
- ))
+ `when`(userTracker.userProfiles)
+ .thenReturn(
+ listOf(
+ UserInfo(USER_ID, "", 0),
+ UserInfo(ENT_USER_ID, "", UserInfo.FLAG_MANAGED_PROFILE)
+ )
+ )
`when`(keyguardStateController.isUnlocked).thenReturn(true)
}
@@ -781,9 +779,7 @@
return user * UserHandle.PER_USER_RANGE + nextUid++
}
- private fun createMockResolveInfo(
- activityInfo: ActivityInfo? = null
- ): ResolveInfo {
+ private fun createMockResolveInfo(activityInfo: ActivityInfo? = null): ResolveInfo {
val resolveInfo = mock(ResolveInfo::class.java)
resolveInfo.activityInfo = activityInfo
return resolveInfo
@@ -822,4 +818,4 @@
`when`(usage.proxyLabel).thenReturn(proxyLabel)
return usage
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 6dc7a06..b24b877 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -63,13 +63,13 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -738,6 +738,44 @@
}
@Test
+ public void onWifiScan_isWifiEnabledFalse_callbackOnWifiScanFalse() {
+ reset(mInternetDialogCallback);
+ when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
+
+ mInternetDialogController.onWifiScan(true);
+
+ verify(mInternetDialogCallback).onWifiScan(false);
+ }
+
+ @Test
+ public void onWifiScan_isDeviceLockedTrue_callbackOnWifiScanFalse() {
+ reset(mInternetDialogCallback);
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+
+ mInternetDialogController.onWifiScan(true);
+
+ verify(mInternetDialogCallback).onWifiScan(false);
+ }
+
+ @Test
+ public void onWifiScan_onWifiScanFalse_callbackOnWifiScanFalse() {
+ reset(mInternetDialogCallback);
+
+ mInternetDialogController.onWifiScan(false);
+
+ verify(mInternetDialogCallback).onWifiScan(false);
+ }
+
+ @Test
+ public void onWifiScan_onWifiScanTrue_callbackOnWifiScanTrue() {
+ reset(mInternetDialogCallback);
+
+ mInternetDialogController.onWifiScan(true);
+
+ verify(mInternetDialogCallback).onWifiScan(true);
+ }
+
+ @Test
public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() {
when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
.thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 039e58a..916bb79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -6,12 +6,10 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -33,9 +31,9 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -48,7 +46,6 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
@@ -613,66 +610,21 @@
}
@Test
- public void showProgressBar_wifiDisabled_hideProgressBar() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
+ public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
+ mInternetDialog.mIsProgressBarVisible = false;
- mInternetDialog.showProgressBar();
+ mInternetDialog.onWifiScan(true);
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
- verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
+ assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
}
@Test
- public void showProgressBar_deviceLocked_hideProgressBar() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+ public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
+ mInternetDialog.mIsProgressBarVisible = true;
- mInternetDialog.showProgressBar();
+ mInternetDialog.onWifiScan(false);
assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
- verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
- }
-
- @Test
- public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
-
- mInternetDialog.showProgressBar();
-
- // Show progress bar
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
-
- ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
- verify(mHandler).postDelayed(runnableCaptor.capture(),
- eq(InternetDialog.PROGRESS_DELAY_MS));
- runnableCaptor.getValue().run();
-
- // Then hide progress bar
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
- }
-
- @Test
- public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() {
- Mockito.reset(mHandler);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = 0;
-
- mInternetDialog.showProgressBar();
-
- // Show progress bar
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
-
- ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
- verify(mHandler).postDelayed(runnableCaptor.capture(),
- eq(InternetDialog.PROGRESS_DELAY_MS));
- runnableCaptor.getValue().run();
-
- // Then hide searching sub-title only
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
- assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index a3cff87e6..76c4015 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -30,8 +30,6 @@
import android.app.ActivityManager;
import android.app.Notification;
-import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -45,12 +43,12 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
-import com.android.systemui.util.time.SystemClockImpl;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,27 +71,24 @@
protected static final int TEST_STICKY_AUTO_DISMISS_TIME = 800;
// Number of notifications to use in tests requiring multiple notifications
private static final int TEST_NUM_NOTIFICATIONS = 4;
- protected static final int TEST_TIMEOUT_TIME = 2_000;
- protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true;
- protected Handler mTestHandler;
protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
- protected final SystemClock mSystemClock = new SystemClockImpl();
- protected boolean mTimedOut = false;
+ protected final FakeSystemClock mSystemClock = new FakeSystemClock();
+ protected final FakeExecutor mExecutor = new FakeExecutor(mSystemClock);
@Mock protected ExpandableNotificationRow mRow;
static {
assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
- assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME);
}
private static class TestableAlertingNotificationManager extends AlertingNotificationManager {
private AlertEntry mLastCreatedEntry;
- private TestableAlertingNotificationManager(Handler handler, SystemClock systemClock) {
- super(new HeadsUpManagerLogger(logcatLogBuffer()), handler, systemClock);
+ private TestableAlertingNotificationManager(SystemClock systemClock,
+ DelayableExecutor executor) {
+ super(new HeadsUpManagerLogger(logcatLogBuffer()), systemClock, executor);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME;
@@ -118,7 +113,7 @@
}
protected AlertingNotificationManager createAlertingNotificationManager() {
- return new TestableAlertingNotificationManager(mTestHandler, mSystemClock);
+ return new TestableAlertingNotificationManager(mSystemClock, mExecutor);
}
protected StatusBarNotification createSbn(int id, Notification n) {
@@ -155,35 +150,6 @@
return new NotificationEntryBuilder().setSbn(createSbn(id)).build();
}
- protected void verifyAlertingAtTime(AlertingNotificationManager anm, NotificationEntry entry,
- boolean shouldBeAlerting, int whenToCheckAlertingMillis, String whenCondition) {
- final Boolean[] wasAlerting = {null};
- final Runnable checkAlerting =
- () -> wasAlerting[0] = anm.isAlerting(entry.getKey());
-
- mTestHandler.postDelayed(checkAlerting, whenToCheckAlertingMillis);
- mTestHandler.postDelayed(mTestTimeoutRunnable, TEST_TIMEOUT_TIME);
- TestableLooper.get(this).processMessages(2);
-
- assertFalse("Test timed out", mTimedOut);
- if (shouldBeAlerting) {
- assertTrue("Should still be alerting after " + whenCondition, wasAlerting[0]);
- } else {
- assertFalse("Should not still be alerting after " + whenCondition, wasAlerting[0]);
- }
- assertFalse("Should not still be alerting after processing",
- anm.isAlerting(entry.getKey()));
- }
-
- @Before
- public void setUp() {
- mTestHandler = Handler.createAsync(Looper.myLooper());
- }
-
- @After
- public void tearDown() {
- mTestHandler.removeCallbacksAndMessages(null);
- }
@Test
public void testShowNotification_addsEntry() {
@@ -203,9 +169,7 @@
final NotificationEntry entry = createEntry(/* id = */ 0);
alm.showNotification(entry);
-
- verifyAlertingAtTime(alm, entry, false, TEST_AUTO_DISMISS_TIME * 3 / 2,
- "auto dismiss time");
+ mSystemClock.advanceTime(TEST_AUTO_DISMISS_TIME * 3 / 2);
assertFalse(alm.isAlerting(entry.getKey()));
}
@@ -217,10 +181,8 @@
alm.showNotification(entry);
- // Try to remove but defer, since the notification has not been shown long enough.
- final boolean removedImmediately = alm.removeNotification(entry.getKey(),
- false /* releaseImmediately */);
-
+ final boolean removedImmediately = alm.removeNotification(
+ entry.getKey(), /* releaseImmediately = */ false);
assertFalse(removedImmediately);
assertTrue(alm.isAlerting(entry.getKey()));
}
@@ -232,10 +194,8 @@
alm.showNotification(entry);
- // Remove forcibly with releaseImmediately = true.
- final boolean removedImmediately = alm.removeNotification(entry.getKey(),
- true /* releaseImmediately */);
-
+ final boolean removedImmediately = alm.removeNotification(
+ entry.getKey(), /* releaseImmediately = */ true);
assertTrue(removedImmediately);
assertFalse(alm.isAlerting(entry.getKey()));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 34c7b09..42c7375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -515,6 +515,29 @@
}
@Test
+ public void testDevicePolicyDoesNotAllowNotifications_deviceOwnerSetsForUserAll() {
+ // User allows them
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ // DevicePolicy hides notifs on lockscreen
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, USER_ALL, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Test
public void testDevicePolicyDoesNotAllowNotifications_userAll() {
// User allows them
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 3fef1d9..5bc75e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -148,6 +148,24 @@
}
@Test
+ fun onWifiEntriesChanged_reasonIsScanResults_fireWifiScanCallbackFalse() {
+ controller.addAccessPointCallback(callback)
+
+ controller.onWifiEntriesChanged(WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS)
+
+ verify(callback).onWifiScan(false)
+ }
+
+ @Test
+ fun onScanRequested_fireWifiScanCallbackTrue() {
+ controller.addAccessPointCallback(callback)
+
+ controller.onScanRequested()
+
+ verify(callback).onWifiScan(true)
+ }
+
+ @Test
fun testOnNumSavedNetworksChangedDoesntTriggerCallback() {
controller.addAccessPointCallback(callback)
controller.onNumSavedNetworksChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 37ee322..e1bd89f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -25,7 +26,6 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -45,11 +45,11 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -88,9 +88,9 @@
StatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
ConfigurationController configurationController,
- Handler handler,
GlobalSettings globalSettings,
SystemClock systemClock,
+ DelayableExecutor executor,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
@@ -104,9 +104,10 @@
groupManager,
visualStabilityProvider,
configurationController,
- handler,
+ mockExecutorHandler(executor),
globalSettings,
systemClock,
+ executor,
accessibilityManagerWrapper,
uiEventLogger,
javaAdapter,
@@ -126,9 +127,9 @@
mStatusBarStateController,
mBypassController,
mConfigurationController,
- mTestHandler,
mGlobalSettings,
mSystemClock,
+ mExecutor,
mAccessibilityManagerWrapper,
mUiEventLogger,
mJavaAdapter,
@@ -142,7 +143,6 @@
}
@Before
- @Override
public void setUp() {
when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
final AccessibilityManagerWrapper accessibilityMgr =
@@ -153,14 +153,6 @@
mDependency.injectMockDependency(NotificationShadeWindowController.class);
mContext.getOrCreateTestableResources().addOverride(
R.integer.ambient_notification_extension_time, 500);
-
- super.setUp();
- }
-
- @After
- @Override
- public void tearDown() {
- super.tearDown();
}
@Test
@@ -224,8 +216,8 @@
hmp.showNotification(entry);
hmp.extendHeadsUp();
+ mSystemClock.advanceTime(TEST_AUTO_DISMISS_TIME + hmp.mExtensionTime / 2);
- final int pastNormalTimeMillis = TEST_AUTO_DISMISS_TIME + hmp.mExtensionTime / 2;
- verifyAlertingAtTime(hmp, entry, true, pastNormalTimeMillis, "normal time");
+ assertTrue(hmp.isAlerting(entry.getKey()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 2940c39..4c893e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -19,6 +19,7 @@
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
import static com.google.common.truth.Truth.assertThat;
@@ -41,7 +42,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Region;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -56,11 +56,10 @@
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,7 +76,6 @@
private static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
private static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
- private static final int TEST_A11Y_TIMEOUT_TIME = 3_000;
private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@@ -87,25 +85,18 @@
assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
-
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan(
- TEST_TIMEOUT_TIME);
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(
- TEST_TIMEOUT_TIME);
- assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan(
- TEST_A11Y_TIMEOUT_TIME);
}
private final class TestableHeadsUpManager extends BaseHeadsUpManager {
TestableHeadsUpManager(Context context,
HeadsUpManagerLogger logger,
- Handler handler,
+ DelayableExecutor executor,
GlobalSettings globalSettings,
SystemClock systemClock,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger) {
- super(context, logger, handler, globalSettings, systemClock,
- accessibilityManagerWrapper, uiEventLogger);
+ super(context, logger, mockExecutorHandler(executor), globalSettings, systemClock,
+ executor, accessibilityManagerWrapper, uiEventLogger);
mTouchAcceptanceDelay = TEST_TOUCH_ACCEPTANCE_TIME;
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
@@ -183,7 +174,7 @@
}
private BaseHeadsUpManager createHeadsUpManager() {
- return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mGlobalSettings,
+ return new TestableHeadsUpManager(mContext, mLogger, mExecutor, mGlobalSettings,
mSystemClock, mAccessibilityMgr, mUiEventLoggerFake);
}
@@ -234,18 +225,6 @@
}
- @Before
- @Override
- public void setUp() {
- super.setUp();
- }
-
- @After
- @Override
- public void tearDown() {
- super.tearDown();
- }
-
@Test
public void testHunRemovedLogging() {
final BaseHeadsUpManager hum = createHeadsUpManager();
@@ -305,10 +284,9 @@
useAccessibilityTimeout(false);
hum.showNotification(entry);
+ mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME);
- final int pastJustAutoDismissMillis =
- TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME;
- verifyAlertingAtTime(hum, entry, true, pastJustAutoDismissMillis, "just auto dismiss");
+ assertTrue(hum.isAlerting(entry.getKey()));
}
@@ -319,10 +297,10 @@
useAccessibilityTimeout(false);
hum.showNotification(entry);
+ mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
+ + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
- final int pastDefaultTimeoutMillis = TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2;
- verifyAlertingAtTime(hum, entry, false, pastDefaultTimeoutMillis, "default timeout");
+ assertFalse(hum.isAlerting(entry.getKey()));
}
@@ -333,10 +311,10 @@
useAccessibilityTimeout(false);
hum.showNotification(entry);
+ mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
+ + (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2);
- final int pastDefaultTimeoutMillis = TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2;
- verifyAlertingAtTime(hum, entry, true, pastDefaultTimeoutMillis, "default timeout");
+ assertTrue(hum.isAlerting(entry.getKey()));
}
@@ -347,18 +325,9 @@
useAccessibilityTimeout(false);
hum.showNotification(entry);
+ mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME);
- final int pastLongestAutoDismissMillis =
- TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME;
- final Boolean[] wasAlerting = {null};
- final Runnable checkAlerting =
- () -> wasAlerting[0] = hum.isAlerting(entry.getKey());
- mTestHandler.postDelayed(checkAlerting, pastLongestAutoDismissMillis);
- TestableLooper.get(this).processMessages(1);
-
- assertTrue("Should still be alerting past longest auto-dismiss", wasAlerting[0]);
- assertTrue("Should still be alerting after processing",
- hum.isAlerting(entry.getKey()));
+ assertTrue(hum.isAlerting(entry.getKey()));
}
@@ -369,10 +338,10 @@
useAccessibilityTimeout(true);
hum.showNotification(entry);
+ mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
+ + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
- final int pastDefaultTimeoutMillis = TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2;
- verifyAlertingAtTime(hum, entry, true, pastDefaultTimeoutMillis, "default timeout");
+ assertTrue(hum.isAlerting(entry.getKey()));
}
@@ -383,10 +352,10 @@
useAccessibilityTimeout(true);
hum.showNotification(entry);
+ mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
+ + (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
- final int pastStickyTimeoutMillis = TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2;
- verifyAlertingAtTime(hum, entry, true, pastStickyTimeoutMillis, "sticky timeout");
+ assertTrue(hum.isAlerting(entry.getKey()));
}
@@ -398,18 +367,14 @@
hum.showNotification(entry);
- // Try to remove but defer, since the notification has not been shown long enough.
final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), false /* releaseImmediately */);
+ entry.getKey(), /* releaseImmediately = */ false);
+ assertFalse(removedImmediately);
+ assertTrue(hum.isAlerting(entry.getKey()));
- assertFalse("HUN should not be removed before minimum display time", removedImmediately);
- assertTrue("HUN should still be alerting before minimum display time",
- hum.isAlerting(entry.getKey()));
+ mSystemClock.advanceTime((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2);
- final int pastMinimumDisplayTimeMillis =
- (TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2;
- verifyAlertingAtTime(hum, entry, false, pastMinimumDisplayTimeMillis,
- "minimum display time");
+ assertFalse(hum.isAlerting(entry.getKey()));
}
@@ -420,32 +385,13 @@
useAccessibilityTimeout(false);
hum.showNotification(entry);
+ mSystemClock.advanceTime((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2);
- // After the minimum display time:
- // 1. Check whether the notification is still alerting.
- // 2. Try to remove it and check whether the remove succeeded.
- // 3. Check whether it is still alerting after trying to remove it.
- final Boolean[] livedPastMinimumDisplayTime = {null};
- final Boolean[] removedAfterMinimumDisplayTime = {null};
- final Boolean[] livedPastRemoveAfterMinimumDisplayTime = {null};
- final Runnable pastMinimumDisplayTimeRunnable = () -> {
- livedPastMinimumDisplayTime[0] = hum.isAlerting(entry.getKey());
- removedAfterMinimumDisplayTime[0] = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false);
- livedPastRemoveAfterMinimumDisplayTime[0] = hum.isAlerting(entry.getKey());
- };
- final int pastMinimumDisplayTimeMillis =
- (TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2;
- mTestHandler.postDelayed(pastMinimumDisplayTimeRunnable, pastMinimumDisplayTimeMillis);
- // Wait until the minimum display time has passed before attempting removal.
- TestableLooper.get(this).processMessages(1);
+ assertTrue(hum.isAlerting(entry.getKey()));
- assertTrue("HUN should live past minimum display time",
- livedPastMinimumDisplayTime[0]);
- assertTrue("HUN should be removed immediately past minimum display time",
- removedAfterMinimumDisplayTime[0]);
- assertFalse("HUN should not live after being removed past minimum display time",
- livedPastRemoveAfterMinimumDisplayTime[0]);
+ final boolean removedImmediately = hum.removeNotification(
+ entry.getKey(), /* releaseImmediately = */ false);
+ assertTrue(removedImmediately);
assertFalse(hum.isAlerting(entry.getKey()));
}
@@ -457,10 +403,8 @@
hum.showNotification(entry);
- // Remove forcibly with releaseImmediately = true.
final boolean removedImmediately = hum.removeNotification(
entry.getKey(), /* releaseImmediately = */ true);
-
assertTrue(removedImmediately);
assertFalse(hum.isAlerting(entry.getKey()));
}
diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
new file mode 100644
index 0000000..b88f302
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.graphics.drawable
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+
+/**
+ * Stub drawable that does nothing. It's to be used in tests as a mock drawable and checked for the
+ * same instance
+ */
+class TestStubDrawable : Drawable() {
+
+ override fun draw(canvas: Canvas) = Unit
+ override fun setAlpha(alpha: Int) = Unit
+ override fun setColorFilter(colorFilter: ColorFilter?) = Unit
+ override fun getOpacity(): Int = PixelFormat.UNKNOWN
+
+ override fun equals(other: Any?): Boolean = this === other
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index 2cb17b5..c85c27e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -1,19 +1,47 @@
package com.android.systemui.communal.data.repository
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.dagger.qualifiers.Background
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.test.TestScope
/** Fake implementation of [CommunalRepository]. */
+@OptIn(ExperimentalCoroutinesApi::class)
class FakeCommunalRepository(
+ @Background applicationScope: CoroutineScope = TestScope(),
override var isCommunalEnabled: Boolean = false,
override val desiredScene: MutableStateFlow<CommunalSceneKey> =
- MutableStateFlow(CommunalSceneKey.Blank)
+ MutableStateFlow(CommunalSceneKey.DEFAULT),
) : CommunalRepository {
override fun setDesiredScene(desiredScene: CommunalSceneKey) {
this.desiredScene.value = desiredScene
}
+ private val defaultTransitionState =
+ ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT)
+ private val _transitionState = MutableStateFlow<Flow<ObservableCommunalTransitionState>?>(null)
+ override val transitionState: StateFlow<ObservableCommunalTransitionState> =
+ _transitionState
+ .flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = defaultTransitionState,
+ )
+
+ override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
+ _transitionState.value = transitionState
+ }
+
fun setIsCommunalEnabled(value: Boolean) {
isCommunalEnabled = value
}
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md
index 30e899c..d84cb67 100644
--- a/ravenwood/api-maintainers.md
+++ b/ravenwood/api-maintainers.md
@@ -71,3 +71,24 @@
The “replace” strategy described above is quite powerful, and can be used in creative ways to sidestep tricky underlying dependencies that aren’t ready yet.
For example, consider a constructor or static initializer that relies on unsupported functionality from another team. By factoring the unsupported logic into a dedicated method, that method can then be replaced under Ravenwood to offer baseline functionality.
+
+## Strategies for JNI
+
+At the moment, JNI isn't yet supported under Ravenwood, but you may still want to support APIs that are partially implemented with JNI. The current approach is to use the “replace” strategy to offer a pure-Java alternative implementation for any JNI-provided logic.
+
+Since this approach requires potentially complex re-implementation, it should only be considered for core infrastructure that is critical to unblocking widespread testing use-cases. Other less-common usages of JNI should instead wait for offical JNI support in the Ravenwood environment.
+
+When a pure-Java implementation grows too large or complex to host within the original class, the `@RavenwoodNativeSubstitutionClass` annotation can be used to host it in a separate source file:
+
+```
+@RavenwoodKeepWholeClass
+@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.MyComplexClass_host")
+public class MyComplexClass {
+ private static native void nativeDoThing(long nativePtr);
+...
+
+public class MyComplexClass_host {
+ public static void nativeDoThing(long nativePtr) {
+ // ...
+ }
+```
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 2ba5f6b..13908f1 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -1,8 +1,16 @@
# Only classes listed here can use the Ravenwood annotations.
com.android.internal.util.ArrayUtils
+com.android.internal.os.BatteryStatsHistory
+com.android.internal.os.BatteryStatsHistory$TraceDelegate
+com.android.internal.os.BatteryStatsHistory$VarintParceler
+com.android.internal.os.BatteryStatsHistoryIterator
+com.android.internal.os.Clock
com.android.internal.os.LongArrayMultiStateCounter
com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer
+com.android.internal.os.MonotonicClock
+com.android.internal.os.PowerStats
+com.android.internal.os.PowerStats$Descriptor
android.util.AtomicFile
android.util.DataUnit
@@ -25,8 +33,15 @@
android.util.Xml
android.os.BatteryConsumer
+android.os.BatteryStats$HistoryItem
+android.os.BatteryStats$HistoryStepDetails
+android.os.BatteryStats$HistoryTag
+android.os.BatteryStats$ProcessStateChange
android.os.Binder
android.os.Binder$IdentitySupplier
+android.os.Broadcaster
+android.os.BundleMerger
+android.os.ConditionVariable
android.os.FileUtils
android.os.FileUtils$MemoryPipe
android.os.Handler
@@ -36,12 +51,15 @@
android.os.Looper
android.os.Message
android.os.MessageQueue
+android.os.PackageTagsList
android.os.Parcel
android.os.Parcelable
android.os.Process
android.os.SystemClock
android.os.ThreadLocalWorkSource
+android.os.TimestampedValue
android.os.UserHandle
+android.os.WorkSource
android.content.ClipData
android.content.ClipData$Item
@@ -95,8 +113,6 @@
com.android.server.LocalServices
-com.android.internal.os.SomeArgs
-
com.android.internal.util.BitUtils
com.android.internal.util.BitwiseInputStream
com.android.internal.util.BitwiseOutputStream
@@ -118,6 +134,23 @@
com.android.internal.util.RingBuffer
com.android.internal.util.StringPool
+com.android.internal.os.BinderCallHeavyHitterWatcher
+com.android.internal.os.BinderDeathDispatcher
+com.android.internal.os.BinderfsStatsReader
+com.android.internal.os.BinderLatencyBuckets
+com.android.internal.os.CachedDeviceState
+com.android.internal.os.Clock
+com.android.internal.os.CpuScalingPolicies
+com.android.internal.os.CpuScalingPolicyReader
+com.android.internal.os.KernelCpuThreadReader
+com.android.internal.os.LoggingPrintStream
+com.android.internal.os.LooperStats
+com.android.internal.os.MonotonicClock
+com.android.internal.os.ProcLocksReader
+com.android.internal.os.ProcStatsUtil
+com.android.internal.os.SomeArgs
+com.android.internal.os.StoragedUidIoStatsReader
+
com.google.android.collect.Lists
com.google.android.collect.Maps
com.google.android.collect.Sets
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index de05777..5df827f 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -74,7 +74,7 @@
$ atest --host MyTestsRavenwood
```
-> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until bug #312525698 is fixed.
+> **Note:** There's a known bug #312525698 where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until the bug is fixed.
You can also run your new tests automatically via `TEST_MAPPING` rules like this:
@@ -89,6 +89,8 @@
}
```
+> **Note:** There's a known bug #308854804 where `TEST_MAPPING` is not being applied, so we're currently planning to run all Ravenwood tests unconditionally in presubmit for changes to `frameworks/base/` and `cts/` until there is a better path forward.
+
## Strategies for feature flags
Ravenwood supports writing tests against logic that uses feature flags through the existing `SetFlagsRule` infrastructure maintained by the feature flagging team:
@@ -112,9 +114,9 @@
## Strategies for migration/bivalent tests
-Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can run on both a real Android device and under a Ravenwood environment.
+Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can be dual-compiled to run on both a real Android device and under a Ravenwood environment.
-In situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly “ignore” that test under Ravenwood, while continuing to validate that test on real devices. Please note that your test must declare a `RavenwoodRule` for the annotation to take effect.
+In situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly “ignore” that test under Ravenwood, while continuing to validate that test on real devices. The annotation can be applied to either individual methods or to an entire test class. Please note that your test class must declare a `RavenwoodRule` for the annotation to take effect.
Test authors are encouraged to provide a `blockedBy` or `reason` argument to help future maintainers understand why a test is being ignored, and under what conditions it might be supported in the future.
@@ -137,11 +139,39 @@
}
```
+At the moment, the `android.content.res.Resources` subsystem isn't yet supported under Ravenwood, but you may still want to dual-compile test suites that depend on references to resources. Below is a strategy for supporting dual-compiliation, where you can "borrow" the generated resource symbols from your traditional `android_test` target:
+
+```
+android_test {
+ name: "MyTestsDevice",
+ resource_dirs: ["res"],
+...
+
+android_ravenwood_test {
+ name: "MyTestsRavenwood",
+ srcs: [
+ ":MyTestsDevice{.aapt.srcjar}",
+...
+```
+
## Strategies for unsupported APIs
As you write tests against Ravenwood, you’ll likely discover API dependencies that aren’t supported yet. Here’s a few strategies that can help you make progress:
-* Your code-under-test may benefit from subtle dependency refactoring to reduce coupling. (For example, providing a specific `File` argument instead of deriving it internally from a `Context`.)
+* Your code-under-test may benefit from subtle dependency refactoring to reduce coupling. (For example, providing a specific `File` argument instead of deriving paths internally from a `Context` or `Environment`.)
+ * One common use-case is providing a directory for your test to store temporary files, which can easily be accomplished using the `Files.createTempDirectory()` API which works on both physical devices and under Ravenwood:
+
+```
+import java.nio.file.Files;
+
+@RunWith(AndroidJUnit4.class)
+public class MyTest {
+ @Before
+ public void setUp() throws Exception {
+ File tempDir = Files.createTempDirectory("MyTest").toFile();
+...
+```
+
* Although mocking code that your team doesn’t own is a generally discouraged testing practice, it can be a valuable pressure relief valve when a dependency isn’t yet supported.
## Strategies for debugging test development
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 0696807..97d36d4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -209,6 +209,7 @@
final ComponentName mComponentName;
int mGenericMotionEventSources;
+ int mObservedMotionEventSources;
// the events pending events to be dispatched to this service
final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>();
@@ -397,6 +398,19 @@
mNotificationTimeout = info.notificationTimeout;
mIsDefault = (info.flags & DEFAULT) != 0;
mGenericMotionEventSources = info.getMotionEventSources();
+ if (android.view.accessibility.Flags.motionEventObserving()) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING)
+ == PackageManager.PERMISSION_GRANTED) {
+ mObservedMotionEventSources = info.getObservedMotionEventSources();
+ } else {
+ Slog.e(
+ LOG_TAG,
+ "Observing motion events requires"
+ + " android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING.");
+ mObservedMotionEventSources = 0;
+ }
+ }
if (supportsFlagForNotImportantViews(info)) {
if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
@@ -1599,7 +1613,7 @@
final int displayId = displays[i].getDisplayId();
onDisplayRemoved(displayId);
}
- if (Flags.cleanupA11yOverlays()) {
+ if (com.android.server.accessibility.Flags.cleanupA11yOverlays()) {
detachAllOverlays();
}
}
@@ -1919,6 +1933,7 @@
return (mGenericMotionEventSources & eventSourceWithoutClass) != 0;
}
+
/**
* Called by the invocation handler to notify the service that the
* state of magnification has changed.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 6cac6a4..9ddc35a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -198,6 +198,7 @@
// State tracking for generic MotionEvents is display-agnostic so we only need one.
private GenericMotionEventStreamState mGenericMotionEventStreamState;
private int mCombinedGenericMotionEventSources = 0;
+ private int mCombinedMotionEventObservedSources = 0;
private EventStreamState mKeyboardStreamState;
@@ -525,16 +526,33 @@
}
if ((mEnabledFeatures & FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS) != 0) {
- addFirstEventHandler(displayId, new BaseEventStreamTransformation() {
- @Override
- public void onMotionEvent(MotionEvent event, MotionEvent rawEvent,
- int policyFlags) {
- if (!anyServiceWantsGenericMotionEvent(rawEvent)
- || !mAms.sendMotionEventToListeningServices(rawEvent)) {
- super.onMotionEvent(event, rawEvent, policyFlags);
- }
- }
- });
+ addFirstEventHandler(
+ displayId,
+ new BaseEventStreamTransformation() {
+ @Override
+ public void onMotionEvent(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ boolean passAlongEvent = true;
+ if (anyServiceWantsGenericMotionEvent(event)) {
+ // Some service wants this event, so try to deliver it to at least
+ // one service.
+ if (mAms.sendMotionEventToListeningServices(event)) {
+ // A service accepted this event, so prevent it from passing
+ // down the stream by default.
+ passAlongEvent = false;
+ }
+ // However, if a service is observing these events instead of
+ // consuming them then ensure
+ // it is always passed along to the next stage of the event stream.
+ if (anyServiceWantsToObserveMotionEvent(event)) {
+ passAlongEvent = true;
+ }
+ }
+ if (passAlongEvent) {
+ super.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ }
+ });
}
if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
@@ -542,15 +560,14 @@
|| ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0)
|| ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
final MagnificationGestureHandler magnificationGestureHandler =
- createMagnificationGestureHandler(displayId,
- displayContext);
+ createMagnificationGestureHandler(displayId, displayContext);
addFirstEventHandler(displayId, magnificationGestureHandler);
mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
}
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
- MotionEventInjector injector = new MotionEventInjector(
- mContext.getMainLooper(), mAms.getTraceManager());
+ MotionEventInjector injector =
+ new MotionEventInjector(mContext.getMainLooper(), mAms.getTraceManager());
addFirstEventHandler(displayId, injector);
mMotionEventInjectors.put(displayId, injector);
}
@@ -923,6 +940,20 @@
}
}
+ private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
+ // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
+ // touch exploration.
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+ && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ return false;
+ }
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedGenericMotionEventSources
+ & mCombinedMotionEventObservedSources
+ & eventSourceWithoutClass)
+ != 0;
+ }
+
private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
// Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
// touch exploration.
@@ -938,6 +969,10 @@
mCombinedGenericMotionEventSources = sources;
}
+ public void setCombinedMotionEventObservedSources(int sources) {
+ mCombinedMotionEventObservedSources = sources;
+ }
+
/**
* Keeps state of streams of events from all keyboard devices.
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 440e996..edb41639 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2825,8 +2825,10 @@
flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
}
int combinedGenericMotionEventSources = 0;
+ int combinedMotionEventObservedSources = 0;
for (AccessibilityServiceConnection connection : userState.mBoundServices) {
combinedGenericMotionEventSources |= connection.mGenericMotionEventSources;
+ combinedMotionEventObservedSources |= connection.mObservedMotionEventSources;
}
if (combinedGenericMotionEventSources != 0) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS;
@@ -2845,6 +2847,8 @@
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
mInputFilter.setCombinedGenericMotionEventSources(
combinedGenericMotionEventSources);
+ mInputFilter.setCombinedMotionEventObservedSources(
+ combinedMotionEventObservedSources);
} else {
if (mHasInputFilter) {
mHasInputFilter = false;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 34787a3..145303d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -554,6 +554,10 @@
if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service");
return;
}
+ if (mRemoteService.getServiceInterface() == null) {
+ if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): remote service is dead or unbound");
+ return;
+ }
final ActivityEvent event = new ActivityEvent(activityId, componentName, type);
if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 20a3b9a..a0ccbf3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,6 +154,7 @@
static_libs: [
"android.frameworks.location.altitude-V1-java", // AIDL
+ "android.frameworks.vibrator-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
@@ -165,7 +166,7 @@
"android.hardware.health-V1.0-java", // HIDL
"android.hardware.health-V2.0-java", // HIDL
"android.hardware.health-V2.1-java", // HIDL
- "android.hardware.health-V2-java", // AIDL
+ "android.hardware.health-V3-java", // AIDL
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.security.rkp-V3-java",
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 708da19..5e9d1cb 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -110,6 +110,9 @@
},
{
"name": "FrameworksNetTests"
+ },
+ {
+ "name": "CtsSuspendAppsTestCases"
}
],
"presubmit-large": [
@@ -150,9 +153,6 @@
"name": "CtsPackageManagerTestCases"
},
{
- "name": "CtsSuspendAppsTestCases"
- },
- {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 3280afdf..627a62e 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -42,6 +42,7 @@
import android.debug.AdbNotifications;
import android.debug.AdbProtoEnums;
import android.debug.AdbTransportType;
+import android.debug.IAdbTransport;
import android.debug.PairDevice;
import android.net.ConnectivityManager;
import android.net.LocalSocket;
@@ -66,6 +67,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.service.adb.AdbDebuggingManagerProto;
+import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.Base64;
import android.util.Slog;
@@ -679,16 +681,17 @@
return;
}
- // Check for network change
- String bssid = wifiInfo.getBSSID();
- if (bssid == null || bssid.isEmpty()) {
- Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
- Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_WIFI_ENABLED, 0);
- return;
- }
synchronized (mAdbConnectionInfo) {
- if (!bssid.equals(mAdbConnectionInfo.getBSSID())) {
+ // Check for network change
+ final String bssid = wifiInfo.getBSSID();
+ if (TextUtils.isEmpty(bssid)) {
+ Slog.e(TAG,
+ "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ return;
+ }
+ if (!TextUtils.equals(bssid, mAdbConnectionInfo.getBSSID())) {
Slog.i(TAG, "Detected wifi network change. Disabling adbwifi.");
Settings.Global.putInt(mContentResolver,
Settings.Global.ADB_WIFI_ENABLED, 0);
@@ -1397,7 +1400,7 @@
}
String bssid = wifiInfo.getBSSID();
- if (bssid == null || bssid.isEmpty()) {
+ if (TextUtils.isEmpty(bssid)) {
Slog.e(TAG, "Unable to get the wifi ap's BSSID.");
return null;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f8f3d82..ace2cfd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -33,6 +33,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.SuppressLint;
+import android.app.AlarmManager;
import android.app.StatsManager;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -427,13 +428,7 @@
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, aggregatedPowerStatsConfig);
mPowerStatsAggregator = new PowerStatsAggregator(aggregatedPowerStatsConfig,
mStats.getHistory());
- final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration);
- final long powerStatsAggregationPeriod = context.getResources().getInteger(
- com.android.internal.R.integer.config_powerStatsAggregationPeriod);
- mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
- aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore,
- Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats);
+ mPowerStatsScheduler = createPowerStatsScheduler(mContext);
PowerStatsExporter powerStatsExporter =
new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator);
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
@@ -445,6 +440,23 @@
mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
}
+ private PowerStatsScheduler createPowerStatsScheduler(Context context) {
+ final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration);
+ final long powerStatsAggregationPeriod = context.getResources().getInteger(
+ com.android.internal.R.integer.config_powerStatsAggregationPeriod);
+ PowerStatsScheduler.AlarmScheduler alarmScheduler =
+ (triggerAtMillis, tag, onAlarmListener, aHandler) -> {
+ AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, tag,
+ onAlarmListener, aHandler);
+ };
+ return new PowerStatsScheduler(mStats::schedulePowerStatsSampleCollection,
+ mPowerStatsAggregator, aggregatedPowerStatsSpanDuration,
+ powerStatsAggregationPeriod, mPowerStatsStore, alarmScheduler, Clock.SYSTEM_CLOCK,
+ mMonotonicClock, () -> mStats.getHistory().getStartTime(), mHandler);
+ }
+
private AggregatedPowerStatsConfig getAggregatedPowerStatsConfig() {
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b2082d9..b4cd6a3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -401,7 +401,7 @@
/**
* All about the process state info (proc state, oom adj score) in this process.
*/
- final ProcessStateRecord mState;
+ ProcessStateRecord mState;
/**
* All about the state info of the optimizer when the process is cached.
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 824bdd4..b182538 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -19,6 +19,7 @@
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
+import static android.server.app.Flags.gameDefaultFrameRate;
import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
@@ -28,6 +29,7 @@
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.Manifest;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -66,11 +68,13 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PermissionEnforcer;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
@@ -138,12 +142,17 @@
static final int WRITE_GAME_MODE_INTERVENTION_LIST_FILE = 6;
static final int WRITE_DELAY_MILLIS = 10 * 1000; // 10 seconds
static final int LOADING_BOOST_MAX_DURATION = 5 * 1000; // 5 seconds
+ static final String PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED =
+ "persist.graphics.game_default_frame_rate.enabled";
+ static final String PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE =
+ "ro.surface_flinger.game_default_frame_rate_override";
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
private static final String USER_ID_MSG_KEY = "userId";
private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
"game_mode_intervention.list";
+
private final Context mContext;
private final Object mLock = new Object();
private final Object mDeviceConfigLock = new Object();
@@ -154,7 +163,6 @@
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final PowerManagerInternal mPowerManagerInternal;
- private final File mSystemDir;
@VisibleForTesting
final AtomicFile mGameModeInterventionListFile;
private DeviceConfigListener mDeviceConfigListener;
@@ -175,28 +183,56 @@
final MyUidObserver mUidObserver;
@GuardedBy("mUidObserverLock")
private final Set<Integer> mForegroundGameUids = new HashSet<>();
+ private final GameManagerServiceSystemPropertiesWrapper mSysProps;
+
+ @VisibleForTesting
+ static class Injector {
+ public GameManagerServiceSystemPropertiesWrapper createSystemPropertiesWrapper() {
+ return new GameManagerServiceSystemPropertiesWrapper() {
+ @Override
+ public String get(String key, String def) {
+ return SystemProperties.get(key, def);
+ }
+ @Override
+ public boolean getBoolean(String key, boolean def) {
+ return SystemProperties.getBoolean(key, def);
+ }
+
+ @Override
+ public int getInt(String key, int def) {
+ return SystemProperties.getInt(key, def);
+ }
+
+ @Override
+ public void set(String key, String val) {
+ SystemProperties.set(key, val);
+ }
+ };
+ }
+ }
public GameManagerService(Context context) {
this(context, createServiceThread().getLooper());
}
GameManagerService(Context context, Looper looper) {
- this(context, looper, Environment.getDataDirectory());
+ this(context, looper, Environment.getDataDirectory(), new Injector());
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- GameManagerService(Context context, Looper looper, File dataDir) {
+ GameManagerService(Context context, Looper looper, File dataDir, Injector injector) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mSystemDir = new File(dataDir, "system");
- mSystemDir.mkdirs();
- FileUtils.setPermissions(mSystemDir.toString(),
+ File systemDir = new File(dataDir, "system");
+ systemDir.mkdirs();
+ FileUtils.setPermissions(systemDir.toString(),
FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
-1, -1);
- mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
+ mGameModeInterventionListFile = new AtomicFile(new File(systemDir,
GAME_MODE_INTERVENTION_LIST_FILE_NAME));
FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
FileUtils.S_IRUSR | FileUtils.S_IWUSR
@@ -220,6 +256,8 @@
} catch (RemoteException e) {
Slog.w(TAG, "Could not register UidObserver");
}
+
+ mSysProps = injector.createSystemPropertiesWrapper();
}
@Override
@@ -1588,7 +1626,7 @@
try {
final float fps = 0.0f;
final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
- setOverrideFrameRate(uid, fps);
+ setGameModeFrameRateOverride(uid, fps);
} catch (PackageManager.NameNotFoundException e) {
return;
}
@@ -1620,7 +1658,7 @@
try {
final float fps = modeConfig.getFps();
final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
- setOverrideFrameRate(uid, fps);
+ setGameModeFrameRateOverride(uid, fps);
} catch (PackageManager.NameNotFoundException e) {
return;
}
@@ -2159,14 +2197,70 @@
}
@VisibleForTesting
- void setOverrideFrameRate(int uid, float frameRate) {
- nativeSetOverrideFrameRate(uid, frameRate);
+ void setGameModeFrameRateOverride(int uid, float frameRate) {
+ nativeSetGameModeFrameRateOverride(uid, frameRate);
+ }
+
+ @VisibleForTesting
+ void setGameDefaultFrameRateOverride(int uid, float frameRate) {
+ Slog.v(TAG, "setDefaultFrameRateOverride : " + uid + " , " + frameRate);
+ nativeSetGameDefaultFrameRateOverride(uid, frameRate);
+ }
+
+ private float getGameDefaultFrameRate() {
+ final boolean isGameDefaultFrameRateEnabled;
+ float gameDefaultFrameRate = 0.0f;
+ synchronized (mLock) {
+ isGameDefaultFrameRateEnabled =
+ mSysProps.getBoolean(
+ PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED, true);
+ }
+ if (gameDefaultFrameRate()) {
+ gameDefaultFrameRate = isGameDefaultFrameRateEnabled
+ ? (float) mSysProps.getInt(
+ PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 0) : 0.0f;
+ }
+ return gameDefaultFrameRate;
+ }
+
+ @Override
+ @EnforcePermission(Manifest.permission.MANAGE_GAME_MODE)
+ public void toggleGameDefaultFrameRate(boolean isEnabled) {
+ toggleGameDefaultFrameRate_enforcePermission();
+ if (gameDefaultFrameRate()) {
+ Slog.v(TAG, "toggleGameDefaultFrameRate : " + isEnabled);
+ this.toggleGameDefaultFrameRateUnchecked(isEnabled);
+ }
+ }
+
+ private void toggleGameDefaultFrameRateUnchecked(boolean isEnabled) {
+ // Update system properties.
+ // Here we only need to immediately update games that are in the foreground.
+ // We will update game default frame rate when a game comes into foreground in
+ // MyUidObserver.
+ synchronized (mLock) {
+ if (isEnabled) {
+ mSysProps.set(
+ PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED, "true");
+ } else {
+ mSysProps.set(
+ PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED, "false");
+ }
+ }
+
+ // Update all foreground games' frame rate.
+ synchronized (mUidObserverLock) {
+ for (int uid : mForegroundGameUids) {
+ setGameDefaultFrameRateOverride(uid, getGameDefaultFrameRate());
+ }
+ }
}
/**
* load dynamic library for frame rate overriding JNI calls
*/
- private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
+ private static native void nativeSetGameModeFrameRateOverride(int uid, float frameRate);
+ private static native void nativeSetGameDefaultFrameRateOverride(int uid, float frameRate);
final class MyUidObserver extends UidObserver {
@Override
@@ -2179,6 +2273,7 @@
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
synchronized (mUidObserverLock) {
+
if (procState != ActivityManager.PROCESS_STATE_TOP) {
disableGameMode(uid);
return;
@@ -2198,6 +2293,7 @@
Slog.v(TAG, "Game power mode ON (process state was changed to foreground)");
mPowerManagerInternal.setPowerMode(Mode.GAME, true);
}
+ setGameDefaultFrameRateOverride(uid, getGameDefaultFrameRate());
mForegroundGameUids.add(uid);
}
}
diff --git a/services/core/java/com/android/server/app/GameManagerServiceSystemPropertiesWrapper.java b/services/core/java/com/android/server/app/GameManagerServiceSystemPropertiesWrapper.java
new file mode 100644
index 0000000..afaceda
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameManagerServiceSystemPropertiesWrapper.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.server.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+/**
+ * Wrapper interface to access {@link SystemProperties}.
+ *
+ * @hide
+ */
+interface GameManagerServiceSystemPropertiesWrapper {
+ /**
+ * Get the String value for the given {@code key}.
+ *
+ * @param key the key to lookup
+ * @param def the default value in case the property is not set or empty
+ * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
+ * string otherwise
+ */
+ @NonNull
+ String get(@NonNull String key, @Nullable String def);
+ /**
+ * Get the Boolean value for the given {@code key}.
+ *
+ * @param key the key to lookup
+ * @param def the default value in case the property is not set or empty
+ * @return if the {@code key} isn't found, return {@code def} if it isn't null, not parsable
+ * or an empty string otherwise
+ */
+ @NonNull
+ boolean getBoolean(@NonNull String key, boolean def);
+
+ /**
+ * Get the Integer value for the given {@code key}.
+ *
+ * @param key the key to lookup
+ * @param def the default value in case the property is not set or empty
+ * @return if the {@code key} isn't found, return {@code def} if it isn't null, not parsable
+ * or an empty string otherwise
+ */
+ @NonNull
+ int getInt(@NonNull String key, int def);
+ /**
+ * Set the value for the given {@code key} to {@code val}.
+ *
+ * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+ * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+ * SELinux. libc will log the underlying reason.
+ */
+ void set(@NonNull String key, @Nullable String val);
+}
diff --git a/services/core/java/com/android/server/app/flags.aconfig b/services/core/java/com/android/server/app/flags.aconfig
new file mode 100644
index 0000000..f2e4783
--- /dev/null
+++ b/services/core/java/com/android/server/app/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.server.app"
+
+flag {
+ name: "game_default_frame_rate"
+ namespace: "game"
+ description: "This flag guards the new behavior with the addition of Game Default Frame Rate feature."
+ bug: "286084594"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 10b6052..e5807e8 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -82,6 +82,7 @@
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -272,9 +273,10 @@
CertPath certPath;
X509Certificate rootCert =
mTestCertHelper.getRootCertificate(rootCertificateAlias);
+ Date validationDate = mTestCertHelper.getValidationDate(rootCertificateAlias);
try {
Log.d(TAG, "Getting and validating a random endpoint certificate");
- certPath = certXml.getRandomEndpointCert(rootCert);
+ certPath = certXml.getRandomEndpointCert(rootCert, validationDate);
} catch (CertValidationException e) {
Log.e(TAG, "Invalid endpoint cert", e);
throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
@@ -348,10 +350,11 @@
X509Certificate rootCert =
mTestCertHelper.getRootCertificate(rootCertificateAlias);
+ Date validationDate = mTestCertHelper.getValidationDate(rootCertificateAlias);
try {
- sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile);
+ sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile, validationDate);
} catch (CertValidationException e) {
- Log.d(TAG, "The signature over the cert file is invalid."
+ Log.e(TAG, "The signature over the cert file is invalid."
+ " Cert: " + HexDump.toHexString(recoveryServiceCertFile)
+ " Sig: " + HexDump.toHexString(recoveryServiceSigFile));
throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
@@ -601,8 +604,9 @@
}
try {
- CertUtils.validateCertPath(
- mTestCertHelper.getRootCertificate(rootCertificateAlias), certPath);
+ Date validationDate = mTestCertHelper.getValidationDate(rootCertificateAlias);
+ CertUtils.validateCertPath(mTestCertHelper.getRootCertificate(rootCertificateAlias),
+ certPath, validationDate);
} catch (CertValidationException e) {
Log.e(TAG, "Failed to validate the given cert path", e);
throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
index c963f79..4a1cae2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -29,6 +29,7 @@
import com.android.internal.widget.LockPatternUtils;
import java.security.cert.X509Certificate;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -67,6 +68,18 @@
return rootCertificate;
}
+ /**
+ * Returns hardcoded validation date for e2e tests.
+ */
+ public @Nullable Date getValidationDate(String rootCertificateAlias) {
+ if (isTestOnlyCertificateAlias(rootCertificateAlias)) {
+ // Certificate used for e2e test is expired.
+ return new Date(2019 - 1900, 1, 30);
+ } else {
+ return null; // Use current time
+ }
+ }
+
public @NonNull String getDefaultCertificateAliasIfEmpty(
@Nullable String rootCertificateAlias) {
if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index 26e8270..0881275 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -305,12 +305,13 @@
*
* @param trustedRoot the trusted root certificate
* @param certPath the certificate path to be validated
+ * @param validationDate use null for current time
* @throws CertValidationException if the given certificate path is invalid, e.g., is expired,
* or does not have a valid signature
*/
- public static void validateCertPath(X509Certificate trustedRoot, CertPath certPath)
- throws CertValidationException {
- validateCertPath(/*validationDate=*/ null, trustedRoot, certPath);
+ public static void validateCertPath(X509Certificate trustedRoot, CertPath certPath,
+ @Nullable Date validationDate) throws CertValidationException {
+ validateCertPath(validationDate, trustedRoot, certPath);
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
index ff22a8d..d159a84 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java
@@ -76,15 +76,16 @@
* and returns the certificate path including the chosen certificate if it is valid.
*
* @param trustedRoot the trusted root certificate
+ * @param validationDate use null for current time
* @return the certificate path including the chosen certificate if the certificate is valid
* @throws CertValidationException if the chosen certificate cannot be validated based on the
* trusted root certificate
*/
- public CertPath getRandomEndpointCert(X509Certificate trustedRoot)
- throws CertValidationException {
+ public CertPath getRandomEndpointCert(X509Certificate trustedRoot,
+ @Nullable Date validationDate)throws CertValidationException {
return getEndpointCert(
new SecureRandom().nextInt(this.endpointCerts.size()),
- /*validationDate=*/ null,
+ validationDate,
trustedRoot);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java
index e75be85..c3f4f55 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/SigXml.java
@@ -18,7 +18,7 @@
import android.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
+import org.w3c.dom.Element;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -26,8 +26,6 @@
import java.util.Date;
import java.util.List;
-import org.w3c.dom.Element;
-
/**
* Parses and holds the XML file containing the signature of the XML file containing the list of THM
* public-key certificates.
@@ -58,17 +56,13 @@
*
* @param trustedRoot the trusted root certificate
* @param signedFileBytes the original file content that has been signed
+ * @param validationDate use null for current time
+ *
* @throws CertValidationException if the signature verification fails, or the signer's
* certificate contained in this XML file cannot be validated
* based on the trusted root certificate
*/
- public void verifyFileSignature(X509Certificate trustedRoot, byte[] signedFileBytes)
- throws CertValidationException {
- verifyFileSignature(trustedRoot, signedFileBytes, /*validationDate=*/ null);
- }
-
- @VisibleForTesting
- void verifyFileSignature(
+ public void verifyFileSignature(
X509Certificate trustedRoot, byte[] signedFileBytes, @Nullable Date validationDate)
throws CertValidationException {
CertUtils.validateCert(validationDate, trustedRoot, intermediateCerts, signerCert);
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 173c452..8504495 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -330,8 +330,11 @@
TextUtils.isEmpty(address)
? null
: mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
- return createMediaRoute2Info(
- routeId, audioDeviceInfo.getType(), audioDeviceInfo.getProductName(), address);
+ // We use the name from the port instead AudioDeviceInfo#getProductName because the latter
+ // replaces empty names with the name of the device (example: Pixel 8). In that case we want
+ // to derive a name ourselves from the type instead.
+ String deviceName = audioDeviceInfo.getPort().name();
+ return createMediaRoute2Info(routeId, audioDeviceInfo.getType(), deviceName, address);
}
/**
@@ -339,8 +342,8 @@
*
* @param routeId A route id, or null to use an id pre-defined for the given {@code type}.
* @param audioDeviceInfoType The type as obtained from {@link AudioDeviceInfo#getType}.
- * @param productName The product name as obtained from {@link
- * AudioDeviceInfo#getProductName()}, or null to use a predefined name for the given {@code
+ * @param deviceName A human readable name to populate the route's {@link
+ * MediaRoute2Info#getName name}, or null to use a predefined name for the given {@code
* type}.
* @param address The type as obtained from {@link AudioDeviceInfo#getAddress()} or {@link
* BluetoothDevice#getAddress()}.
@@ -350,7 +353,7 @@
private MediaRoute2Info createMediaRoute2Info(
@Nullable String routeId,
int audioDeviceInfoType,
- @Nullable CharSequence productName,
+ @Nullable CharSequence deviceName,
@Nullable String address) {
SystemRouteInfo systemRouteInfo =
AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.get(audioDeviceInfoType);
@@ -359,7 +362,7 @@
// earpiece.
return null;
}
- CharSequence humanReadableName = productName;
+ CharSequence humanReadableName = deviceName;
if (TextUtils.isEmpty(humanReadableName)) {
humanReadableName = mContext.getResources().getText(systemRouteInfo.mNameResource);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c2a145d..a62d8b8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -119,6 +119,7 @@
import static android.service.notification.NotificationListenerService.Ranking.RANKING_UNCHANGED;
import static android.service.notification.NotificationListenerService.TRIM_FULL;
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
+import static android.view.contentprotection.flags.Flags.rapidClearNotificationsByListenerAppOpEnabled;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
@@ -414,6 +415,12 @@
static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
+ /**
+ * The threshold, in milliseconds, to determine whether a notification has been
+ * cleared too quickly.
+ */
+ private static final int NOTIFICATION_RAPID_CLEAR_THRESHOLD_MS = 5000;
+
static final int INVALID_UID = -1;
static final String ROOT_PKG = "root";
@@ -4817,9 +4824,12 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long identity = Binder.clearCallingIdentity();
+ boolean notificationsRapidlyCleared = false;
+ final String pkg;
try {
synchronized (mNotificationLock) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ pkg = info.component.getPackageName();
// Cancellation reason. If the token comes from assistant, label the
// cancellation as coming from the assistant; default to LISTENER_CANCEL.
@@ -4838,11 +4848,19 @@
!mUserProfiles.isCurrentProfile(userId)) {
continue;
}
+ notificationsRapidlyCleared = notificationsRapidlyCleared
+ || isNotificationRecent(r);
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
r.getSbn().getPackageName(), r.getSbn().getTag(),
r.getSbn().getId(), userId, reason);
}
} else {
+ for (NotificationRecord notificationRecord : mNotificationList) {
+ if (isNotificationRecent(notificationRecord)) {
+ notificationsRapidlyCleared = true;
+ break;
+ }
+ }
if (lifetimeExtensionRefactor()) {
cancelAllLocked(callingUid, callingPid, info.userid,
REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
@@ -4855,11 +4873,23 @@
}
}
}
+ if (notificationsRapidlyCleared) {
+ mAppOps.noteOpNoThrow(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
+ callingUid, pkg, /* attributionTag= */ null, /* message= */ null);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+ private boolean isNotificationRecent(@NonNull NotificationRecord notificationRecord) {
+ if (!rapidClearNotificationsByListenerAppOpEnabled()) {
+ return false;
+ }
+ return notificationRecord.getFreshnessMs(System.currentTimeMillis())
+ < NOTIFICATION_RAPID_CLEAR_THRESHOLD_MS;
+ }
+
/**
* Handle request from an approved listener to re-enable itself.
*
@@ -5334,14 +5364,7 @@
@Override
public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) {
- Objects.requireNonNull(automaticZenRule, "automaticZenRule is null");
- Objects.requireNonNull(automaticZenRule.getName(), "Name is null");
- if (automaticZenRule.getOwner() == null
- && automaticZenRule.getConfigurationActivity() == null) {
- throw new NullPointerException(
- "Rule must have a conditionproviderservice and/or configuration activity");
- }
- Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
+ validateAutomaticZenRule(automaticZenRule);
checkCallerIsSameApp(pkg);
if (automaticZenRule.getZenPolicy() != null
&& automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
@@ -5368,16 +5391,8 @@
}
@Override
- public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
- throws RemoteException {
- Objects.requireNonNull(automaticZenRule, "automaticZenRule is null");
- Objects.requireNonNull(automaticZenRule.getName(), "Name is null");
- if (automaticZenRule.getOwner() == null
- && automaticZenRule.getConfigurationActivity() == null) {
- throw new NullPointerException(
- "Rule must have a conditionproviderservice and/or configuration activity");
- }
- Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
+ public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule) {
+ validateAutomaticZenRule(automaticZenRule);
enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
// TODO: b/308670715: Distinguish origin properly (e.g. USER if updating a rule
@@ -5388,6 +5403,29 @@
"updateAutomaticZenRule", Binder.getCallingUid());
}
+ private void validateAutomaticZenRule(AutomaticZenRule rule) {
+ Objects.requireNonNull(rule, "automaticZenRule is null");
+ Objects.requireNonNull(rule.getName(), "Name is null");
+ if (rule.getOwner() == null
+ && rule.getConfigurationActivity() == null) {
+ throw new NullPointerException(
+ "Rule must have a conditionproviderservice and/or configuration activity");
+ }
+ Objects.requireNonNull(rule.getConditionId(), "ConditionId is null");
+
+ if (android.app.Flags.modesApi()) {
+ if (rule.getType() == AutomaticZenRule.TYPE_MANAGED) {
+ int uid = Binder.getCallingUid();
+ boolean isDeviceOwner = Binder.withCleanCallingIdentity(
+ () -> mDpm.isActiveDeviceOwner(uid));
+ if (!isDeviceOwner) {
+ throw new IllegalArgumentException(
+ "Only Device Owners can use AutomaticZenRules with TYPE_MANAGED");
+ }
+ }
+ }
+ }
+
@Override
public boolean removeAutomaticZenRule(String id) throws RemoteException {
Objects.requireNonNull(id, "Id is null");
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index dcac8c9..da01745 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -20,3 +20,17 @@
description: "This flag controls the refactoring of NMS to NotificationAttentionHelper"
bug: "291907312"
}
+
+flag {
+ name: "cross_app_polite_notifications"
+ namespace: "systemui"
+ description: "This flag controls the cross-app effect of polite notifications"
+ bug: "270456865"
+}
+
+flag {
+ name: "vibrate_while_unlocked"
+ namespace: "systemui"
+ description: "This flag controls the vibrate while unlocked setting of polite notifications"
+ bug: "270456865"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index abfe9de..e1eb8f0 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -20,7 +20,6 @@
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.PersistableBundle;
-import android.util.FastImmutableArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -30,8 +29,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.function.Consumer;
-import java.util.stream.Stream;
/**
* Collects snapshots of power-related system statistics.
@@ -246,8 +246,7 @@
@GuardedBy("this")
@SuppressWarnings("unchecked")
- private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList =
- new FastImmutableArraySet<Consumer<PowerStats>>(new Consumer[0]);
+ private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList();
public PowerStatsCollector(Handler handler, long throttlePeriodMs, Clock clock) {
mHandler = handler;
@@ -262,9 +261,13 @@
@SuppressWarnings("unchecked")
public void addConsumer(Consumer<PowerStats> consumer) {
synchronized (this) {
- mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>(
- Stream.concat(mConsumerList.stream(), Stream.of(consumer))
- .toArray(Consumer[]::new));
+ if (mConsumerList.contains(consumer)) {
+ return;
+ }
+
+ List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList);
+ newList.add(consumer);
+ mConsumerList = Collections.unmodifiableList(newList);
}
}
@@ -275,9 +278,9 @@
@SuppressWarnings("unchecked")
public void removeConsumer(Consumer<PowerStats> consumer) {
synchronized (this) {
- mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>(
- mConsumerList.stream().filter(c -> c != consumer)
- .toArray(Consumer[]::new));
+ List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList);
+ newList.remove(consumer);
+ mConsumerList = Collections.unmodifiableList(newList);
}
}
@@ -302,8 +305,9 @@
if (stats == null) {
return;
}
- for (Consumer<PowerStats> consumer : mConsumerList) {
- consumer.accept(stats);
+ List<Consumer<PowerStats>> consumerList = mConsumerList;
+ for (int i = consumerList.size() - 1; i >= 0; i--) {
+ consumerList.get(i).accept(stats);
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index 97d872a..121a98b 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -18,7 +18,6 @@
import android.annotation.DurationMillisLong;
import android.app.AlarmManager;
-import android.content.Context;
import android.os.ConditionVariable;
import android.os.Handler;
import android.util.IndentingPrintWriter;
@@ -30,6 +29,7 @@
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
/**
* Controls the frequency at which {@link PowerStatsSpan}'s are generated and stored in
@@ -39,7 +39,7 @@
private static final long MINUTE_IN_MILLIS = TimeUnit.MINUTES.toMillis(1);
private static final long HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
- private final Context mContext;
+ private final AlarmScheduler mAlarmScheduler;
private boolean mEnablePeriodicPowerStatsCollection;
@DurationMillisLong
private final long mAggregatedPowerStatsSpanDuration;
@@ -49,24 +49,38 @@
private final Clock mClock;
private final MonotonicClock mMonotonicClock;
private final Handler mHandler;
- private final BatteryStatsImpl mBatteryStats;
+ private final Runnable mPowerStatsCollector;
+ private final Supplier<Long> mEarliestAvailableBatteryHistoryTimeMs;
private final PowerStatsAggregator mPowerStatsAggregator;
private long mLastSavedSpanEndMonotonicTime;
- public PowerStatsScheduler(Context context, PowerStatsAggregator powerStatsAggregator,
+ /**
+ * External dependency on AlarmManager.
+ */
+ public interface AlarmScheduler {
+ /**
+ * Should use AlarmManager to schedule an inexact, non-wakeup alarm.
+ */
+ void scheduleAlarm(long triggerAtMillis, String tag,
+ AlarmManager.OnAlarmListener onAlarmListener, Handler handler);
+ }
+
+ public PowerStatsScheduler(Runnable powerStatsCollector,
+ PowerStatsAggregator powerStatsAggregator,
@DurationMillisLong long aggregatedPowerStatsSpanDuration,
@DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
- Clock clock, MonotonicClock monotonicClock, Handler handler,
- BatteryStatsImpl batteryStats) {
- mContext = context;
+ AlarmScheduler alarmScheduler, Clock clock, MonotonicClock monotonicClock,
+ Supplier<Long> earliestAvailableBatteryHistoryTimeMs, Handler handler) {
mPowerStatsAggregator = powerStatsAggregator;
mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
mPowerStatsAggregationPeriod = powerStatsAggregationPeriod;
mPowerStatsStore = powerStatsStore;
+ mAlarmScheduler = alarmScheduler;
mClock = clock;
mMonotonicClock = monotonicClock;
mHandler = handler;
- mBatteryStats = batteryStats;
+ mPowerStatsCollector = powerStatsCollector;
+ mEarliestAvailableBatteryHistoryTimeMs = earliestAvailableBatteryHistoryTimeMs;
}
/**
@@ -81,9 +95,8 @@
}
private void scheduleNextPowerStatsAggregation() {
- AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
- alarmManager.set(AlarmManager.ELAPSED_REALTIME,
- mClock.elapsedRealtime() + mPowerStatsAggregationPeriod, "PowerStats",
+ mAlarmScheduler.scheduleAlarm(mClock.elapsedRealtime() + mPowerStatsAggregationPeriod,
+ "PowerStats",
() -> {
schedulePowerStatsAggregation();
mHandler.post(this::scheduleNextPowerStatsAggregation);
@@ -96,7 +109,7 @@
@VisibleForTesting
public void schedulePowerStatsAggregation() {
// Catch up the power stats collectors
- mBatteryStats.schedulePowerStatsSampleCollection();
+ mPowerStatsCollector.run();
mHandler.post(this::aggregateAndStorePowerStats);
}
@@ -105,7 +118,7 @@
long currentMonotonicTime = mMonotonicClock.monotonicTime();
long startTime = getLastSavedSpanEndMonotonicTime();
if (startTime < 0) {
- startTime = mBatteryStats.getHistory().getStartTime();
+ startTime = mEarliestAvailableBatteryHistoryTimeMs.get();
}
long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
new file mode 100644
index 0000000..2eeb903
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -0,0 +1,105 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.frameworks.vibrator.IVibratorControlService;
+import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.VibrationParam;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Implementation of {@link IVibratorControlService} which allows the registration of
+ * {@link IVibratorController} to set and receive vibration params.
+ *
+ * @hide
+ */
+public final class VibratorControlService extends IVibratorControlService.Stub {
+ private static final String TAG = "VibratorControlService";
+
+ private final VibratorControllerHolder mVibratorControllerHolder;
+ private final Object mLock;
+
+ public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
+ mVibratorControllerHolder = vibratorControllerHolder;
+ mLock = lock;
+ }
+
+ @Override
+ public void registerVibratorController(IVibratorController controller)
+ throws RemoteException {
+ synchronized (mLock) {
+ mVibratorControllerHolder.setVibratorController(controller);
+ }
+ }
+
+ @Override
+ public void unregisterVibratorController(@NonNull IVibratorController controller)
+ throws RemoteException {
+ Objects.requireNonNull(controller);
+
+ synchronized (mLock) {
+ if (mVibratorControllerHolder.getVibratorController() == null) {
+ Slog.w(TAG, "Received request to unregister IVibratorController = "
+ + controller + ", but no controller was previously registered. Request "
+ + "Ignored.");
+ return;
+ }
+ if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+ controller.asBinder())) {
+ Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided "
+ + "controller doesn't match the registered one. " + this);
+ return;
+ }
+ mVibratorControllerHolder.setVibratorController(null);
+ }
+ }
+
+ @Override
+ public void setVibrationParams(
+ @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
+ throws RemoteException {
+ // TODO(b/305939964): Add set vibration implementation.
+ }
+
+ @Override
+ public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
+ // TODO(b/305939964): Add clear vibration implementation.
+ }
+
+ @Override
+ public void onRequestVibrationParamsComplete(
+ IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
+ throws RemoteException {
+ // TODO(305942827): Cache the vibration params in VibrationScaler
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return this.HASH;
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
new file mode 100644
index 0000000..63e69db
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
@@ -0,0 +1,70 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.frameworks.vibrator.IVibratorController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Holder class for {@link IVibratorController}.
+ *
+ * @hide
+ */
+public final class VibratorControllerHolder implements IBinder.DeathRecipient {
+ private static final String TAG = "VibratorControllerHolder";
+
+ private IVibratorController mVibratorController;
+
+ public IVibratorController getVibratorController() {
+ return mVibratorController;
+ }
+
+ /**
+ * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new
+ * controller. This will also take care of registering and unregistering death notifications
+ * for the cached {@link IVibratorController}.
+ */
+ public void setVibratorController(IVibratorController controller) {
+ try {
+ if (mVibratorController != null) {
+ mVibratorController.asBinder().unlinkToDeath(this, 0);
+ }
+ mVibratorController = controller;
+ if (mVibratorController != null) {
+ mVibratorController.asBinder().linkToDeath(this, 0);
+ }
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e);
+ }
+ }
+
+ @Override
+ public void binderDied(@NonNull IBinder deadBinder) {
+ if (deadBinder == mVibratorController.asBinder()) {
+ setVibratorController(null);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Should not be used as binderDied(IBinder who) is overridden.
+ Slog.wtf(TAG, "binderDied() called unexpectedly.");
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 7d4bd3b..fc824ab 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -91,6 +91,8 @@
public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String TAG = "VibratorManagerService";
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
+ private static final String VIBRATOR_CONTROL_SERVICE =
+ "android.frameworks.vibrator.IVibratorControlService/default";
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -269,6 +271,11 @@
context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
+ if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
+ injector.addService(VIBRATOR_CONTROL_SERVICE,
+ new VibratorControlService(new VibratorControllerHolder(), mLock));
+ }
+
}
/** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 03d6c2c..ae10ce3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5149,8 +5149,13 @@
/** @return the orientation of the display when it's rotation is ROTATION_0. */
int getNaturalOrientation() {
- return mBaseDisplayWidth < mBaseDisplayHeight
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ final Configuration config = getConfiguration();
+ if (config.windowConfiguration.getDisplayRotation() == ROTATION_0) {
+ return config.orientation;
+ }
+ final Rect frame = mDisplayPolicy.getDecorInsetsInfo(
+ ROTATION_0, mBaseDisplayWidth, mBaseDisplayHeight).mConfigFrame;
+ return frame.width() <= frame.height() ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
}
void performLayout(boolean initial, boolean updateInputWindows) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a872fd0..4b99432 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -63,6 +63,7 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
@@ -1178,6 +1179,14 @@
mService.mRootWindowContainer.moveActivityToPinnedRootTask(
pipActivity, null /* launchIntoPipHostActivity */,
"moveActivityToPinnedRootTask", null /* transition */, entryBounds);
+
+ // Continue the pausing process after potential task reparenting.
+ if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) {
+ pipActivity.getTask().schedulePauseActivity(
+ pipActivity, false /* userLeaving */,
+ false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip");
+ }
+
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index fd5f7a3..5721750 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -66,6 +66,7 @@
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Build;
+import android.os.DeadObjectException;
import android.os.FactoryTest;
import android.os.LocaleList;
import android.os.Message;
@@ -1675,6 +1676,10 @@
@NonNull ClientTransactionItem transactionItem) {
try {
mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
+ } catch (DeadObjectException e) {
+ // Expected if the process has been killed.
+ Slog.w(TAG_CONFIGURATION, "Failed for dead process. ClientTransactionItem="
+ + transactionItem + " owner=" + mOwner);
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule ClientTransactionItem="
+ transactionItem + " owner=" + mOwner, e);
diff --git a/services/core/jni/com_android_server_app_GameManagerService.cpp b/services/core/jni/com_android_server_app_GameManagerService.cpp
index 3028813..f593f40 100644
--- a/services/core/jni/com_android_server_app_GameManagerService.cpp
+++ b/services/core/jni/com_android_server_app_GameManagerService.cpp
@@ -25,15 +25,21 @@
namespace android {
-static void android_server_app_GameManagerService_nativeSetOverrideFrameRate(JNIEnv* env,
- jclass clazz, jint uid,
- jfloat frameRate) {
- SurfaceComposerClient::setOverrideFrameRate(uid, frameRate);
+static void android_server_app_GameManagerService_nativeSetGameModeFrameRateOverride(
+ JNIEnv* env, jclass clazz, jint uid, jfloat frameRate) {
+ SurfaceComposerClient::setGameModeFrameRateOverride(uid, frameRate);
+}
+
+static void android_server_app_GameManagerService_nativeSetGameDefaultFrameRateOverride(
+ JNIEnv* env, jclass clazz, jint uid, jfloat frameRate) {
+ SurfaceComposerClient::setGameDefaultFrameRateOverride(uid, frameRate);
}
static const JNINativeMethod gMethods[] = {
- {"nativeSetOverrideFrameRate", "(IF)V",
- (void*)android_server_app_GameManagerService_nativeSetOverrideFrameRate},
+ {"nativeSetGameModeFrameRateOverride", "(IF)V",
+ (void*)android_server_app_GameManagerService_nativeSetGameModeFrameRateOverride},
+ {"nativeSetGameDefaultFrameRateOverride", "(IF)V",
+ (void*)android_server_app_GameManagerService_nativeSetGameDefaultFrameRateOverride},
};
int register_android_server_app_GameManagerService(JNIEnv* env) {
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
index 47c2a1b..29e258c 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -5,4 +5,12 @@
namespace: "display_manager"
description: "Feature flag for dual display blocking"
bug: "278667199"
+}
+
+flag {
+ name: "enable_foldables_posture_based_closed_state"
+ namespace: "windowing_frontend"
+ description: "Enables smarter closed device state state for foldable devices"
+ bug: "309792734"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 7638915..eae159f 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,9 @@
<version>1</version>
<fqname>IAltitudeService/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.frameworks.vibrator</name>
+ <version>1</version>
+ <fqname>IVibratorControlService/default</fqname>
+ </hal>
</manifest>
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index ea5fb5d6..a0fb013 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,9 +16,7 @@
package com.android.server.permission.access.permission
-import android.Manifest
import android.permission.PermissionManager
-import android.permission.flags.Flags
import android.util.Slog
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
@@ -61,7 +59,7 @@
}
}
- fun MutateStateScope.removeInactiveDevicesPermission(activePersistentDeviceIds: Set<String>) {
+ fun MutateStateScope.trimDevicePermissionStates(deviceIds: Set<String>) {
newState.userStates.forEachIndexed { _, userId, userState ->
userState.appIdDevicePermissionFlags.forEachReversedIndexed { _, appId, _ ->
val appIdDevicePermissionFlags =
@@ -69,14 +67,11 @@
val devicePermissionFlags =
appIdDevicePermissionFlags.mutate(appId) ?: return@forEachReversedIndexed
- val removePersistentDeviceIds = mutableSetOf<String>()
- devicePermissionFlags.forEachIndexed { _, deviceId, _ ->
- if (!activePersistentDeviceIds.contains(deviceId)) {
- removePersistentDeviceIds.add(deviceId)
+ devicePermissionFlags.forEachReversedIndexed { _, deviceId, _ ->
+ if (deviceId !in deviceIds) {
+ devicePermissionFlags -= deviceId
}
}
-
- removePersistentDeviceIds.forEach { deviceId -> devicePermissionFlags -= deviceId }
}
}
}
@@ -122,6 +117,10 @@
resetRuntimePermissions(packageName, userId)
}
+ /**
+ * Reset permission states for all permissions requested by the given package, if no other
+ * package (sharing the App ID) request these permissions.
+ */
fun MutateStateScope.resetRuntimePermissions(packageName: String, userId: Int) {
// It's okay to skip resetting permissions for packages that are removed,
// because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
@@ -144,6 +143,7 @@
}
}
+ // Trims permission state for permissions not requested by the App ID anymore.
private fun MutateStateScope.trimPermissionStates(appId: Int) {
val requestedPermissions = MutableIndexedSet<String>()
forEachPackageInAppId(appId) {
@@ -245,10 +245,6 @@
flagMask: Int,
flagValues: Int
): Boolean {
- if (!isDeviceAwarePermission(permissionName)) {
- Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
- return false
- }
val oldFlags =
newState.userStates[userId]!!
.appIdDevicePermissionFlags[appId]
@@ -295,20 +291,8 @@
synchronized(listenersLock) { listeners = listeners + listener }
}
- private fun isDeviceAwarePermission(permissionName: String): Boolean =
- DEVICE_AWARE_PERMISSIONS.contains(permissionName)
-
companion object {
private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
-
- /** These permissions are supported for virtual devices. */
- // TODO: b/298661870 - Use new API to get the list of device aware permissions.
- val DEVICE_AWARE_PERMISSIONS =
- if (Flags.deviceAwarePermissionApisEnabled()) {
- setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
- } else {
- emptySet<String>()
- }
}
/** Listener for permission flags changes. */
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 0f65494..f469ab5 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
@@ -1555,7 +1555,7 @@
deviceId == Context.DEVICE_ID_DEFAULT) {
with(policy) { getPermissionFlags(appId, userId, permissionName) }
} else {
- if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
+ if (permissionName !in DEVICE_AWARE_PERMISSIONS) {
Slog.i(
LOG_TAG,
"$permissionName is not device aware permission, " +
@@ -1591,7 +1591,7 @@
deviceId == Context.DEVICE_ID_DEFAULT) {
with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
} else {
- if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
+ if (permissionName !in DEVICE_AWARE_PERMISSIONS) {
Slog.i(
LOG_TAG,
"$permissionName is not device aware permission, " +
@@ -2314,20 +2314,19 @@
override fun onSystemReady() {
service.onSystemReady()
+
virtualDeviceManagerInternal =
LocalServices.getService(VirtualDeviceManagerInternal::class.java)
-
virtualDeviceManagerInternal?.allPersistentDeviceIds?.let { persistentDeviceIds ->
service.mutateState {
- with(devicePolicy) { removeInactiveDevicesPermission(persistentDeviceIds) }
+ with(devicePolicy) { trimDevicePermissionStates(persistentDeviceIds) }
}
}
-
- // trim permission states for the external devices, when they are removed.
virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { persistentDeviceId
->
service.mutateState { with(devicePolicy) { onDeviceIdRemoved(persistentDeviceId) } }
}
+
permissionControllerManager =
PermissionControllerManager(context, PermissionThread.getHandler())
}
@@ -2862,5 +2861,14 @@
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
+
+ /** These permissions are supported for virtual devices. */
+ // TODO: b/298661870 - Use new API to get the list of device aware permissions.
+ val DEVICE_AWARE_PERMISSIONS =
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
+ } else {
+ emptySet<String>()
+ }
}
}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt
index 832136c..925dad8 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt
@@ -38,7 +38,7 @@
* AppIdPermissionPolicyPermissionStatesTest because these concepts don't apply to onUserAdded().
*/
@RunWith(Parameterized::class)
-class AppIdPermissionPolicyPermissionDefinitionsTest : BaseAppIdPermissionPolicyTest() {
+class AppIdPermissionPolicyPermissionDefinitionsTest : BasePermissionPolicyTest() {
@Parameterized.Parameter(0) lateinit var action: Action
@Test
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
index 0547719..1237095 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
@@ -29,7 +29,7 @@
* and resetRuntimePermissions() in AppIdPermissionPolicy
*/
@RunWith(Parameterized::class)
-class AppIdPermissionPolicyPermissionResetTest : BaseAppIdPermissionPolicyTest() {
+class AppIdPermissionPolicyPermissionResetTest : BasePermissionPolicyTest() {
@Parameterized.Parameter(0) lateinit var action: Action
@Test
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
index c44b2c5..6b9c9c2 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
@@ -39,7 +39,7 @@
* states for onUserAdded(), onStorageVolumeAdded() and onPackageAdded() in AppIdPermissionPolicy
*/
@RunWith(Parameterized::class)
-class AppIdPermissionPolicyPermissionStatesTest : BaseAppIdPermissionPolicyTest() {
+class AppIdPermissionPolicyPermissionStatesTest : BasePermissionPolicyTest() {
@Parameterized.Parameter(0) lateinit var action: Action
@Before
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
index e4e3368..cde46ab 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -30,7 +30,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-class AppIdPermissionPolicyTest : BaseAppIdPermissionPolicyTest() {
+class AppIdPermissionPolicyTest : BasePermissionPolicyTest() {
@Test
fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
similarity index 99%
rename from services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
rename to services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
index 316f338..7b3f216 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BasePermissionPolicyTest.kt
@@ -53,7 +53,7 @@
* Mocking unit test for AppIdPermissionPolicy.
*/
@RunWith(AndroidJUnit4::class)
-abstract class BaseAppIdPermissionPolicyTest {
+abstract class BasePermissionPolicyTest {
protected lateinit var oldState: MutableAccessState
protected lateinit var newState: MutableAccessState
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/DevicePermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/DevicePermissionPolicyTest.kt
new file mode 100644
index 0000000..996dd9d
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/DevicePermissionPolicyTest.kt
@@ -0,0 +1,282 @@
+/*
+ * 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.permission.test
+
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutableDevicePermissionFlags
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.permission.DevicePermissionPolicy
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.testutils.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+/**
+ * This class tests permissions for external devices, we have separate policy to
+ * manage external device permissions.
+ */
+class DevicePermissionPolicyTest : BasePermissionPolicyTest() {
+ private val devicePermissionPolicy = DevicePermissionPolicy()
+
+ @Test
+ fun testOnAppIdRemoved_clearPermissionFlags() {
+ val packageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(packageState)
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ with(devicePermissionPolicy) {
+ onAppIdRemoved(APP_ID_1)
+ }
+ }
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(0)
+ }
+
+ @Test
+ fun testOnDeviceIdRemoved_clearPermissionFlags() {
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(requestingPackageState)
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_2, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_2, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ with(devicePermissionPolicy) {
+ onDeviceIdRemoved(DEVICE_ID_1)
+ }
+ }
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(0)
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_2, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ }
+
+ @Test
+ fun testRemoveInactiveDevicesPermission_clearPermissionFlags() {
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(requestingPackageState)
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_2, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_2, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ with(devicePermissionPolicy) {
+ trimDevicePermissionStates(setOf(DEVICE_ID_2))
+ }
+ }
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(0)
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_2, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ }
+
+ @Test
+ fun testOnStateMutated_notEmpty_isCalledForEachListener() {
+ val mockListener =
+ mock<DevicePermissionPolicy.OnDevicePermissionFlagsChangedListener> {}
+ devicePermissionPolicy.addOnPermissionFlagsChangedListener(mockListener)
+
+ GetStateScope(oldState).apply {
+ with(devicePermissionPolicy) {
+ onStateMutated()
+ }
+ }
+
+ verify(mockListener, times(1)).onStateMutated()
+ }
+
+ @Test
+ fun testOnStorageVolumeMounted_trimsPermissionsNotRequestAnymore() {
+ val packageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_1, PERMISSION_NAME_0)
+ )
+ )
+ addPackageState(packageState)
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1, PermissionFlags.RUNTIME_GRANTED
+ )
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_1))
+ )
+ addPackageState(installedPackageState)
+
+ mutateState {
+ with(devicePermissionPolicy) {
+ onStorageVolumeMounted(null, listOf(PACKAGE_NAME_1), false)
+ }
+ }
+
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1))
+ .isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(0)
+ }
+
+
+ @Test
+ fun testResetRuntimePermissions_trimsPermissionStates() {
+ val packageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_1))
+ )
+ addPackageState(packageState)
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1, PermissionFlags.RUNTIME_GRANTED
+ )
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ with(devicePermissionPolicy) {
+ resetRuntimePermissions(PACKAGE_NAME_1, USER_ID_0)
+ }
+ }
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1))
+ .isEqualTo(0)
+ }
+
+ @Test
+ fun testResetRuntimePermissions_keepsPermissionStates() {
+ val packageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(PERMISSION_NAME_1, PERMISSION_NAME_0)
+ )
+ )
+ val packageState2 = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_2, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(packageState)
+ addPackageState(packageState2)
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1, PermissionFlags.RUNTIME_GRANTED
+ )
+ setPermissionFlags(
+ APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED
+ )
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ assertThat(
+ getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0, oldState)
+ ).isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ with(devicePermissionPolicy) {
+ resetRuntimePermissions(PACKAGE_NAME_1, USER_ID_0)
+ }
+ }
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_1))
+ .isEqualTo(0)
+ assertThat(getPermissionFlags(APP_ID_1, DEVICE_ID_1, USER_ID_0, PERMISSION_NAME_0))
+ .isEqualTo(PermissionFlags.RUNTIME_GRANTED)
+ }
+
+ private fun getPermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String,
+ state: MutableAccessState = newState
+ ): Int = state.userStates[userId]
+ ?.appIdDevicePermissionFlags
+ ?.get(appId)
+ ?.get(deviceId)
+ ?.getWithDefault(permissionName, 0)
+ ?: 0
+
+
+ private fun setPermissionFlags(
+ appId: Int,
+ deviceId: String,
+ userId: Int,
+ permissionName: String,
+ newFlags: Int,
+ state: MutableAccessState = oldState
+ ) {
+ val appIdDevicePermissionFlags =
+ state.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
+ val devicePermissionFlags =
+ appIdDevicePermissionFlags.mutateOrPut(appId) { MutableDevicePermissionFlags() }
+ val permissionFlags =
+ devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
+ permissionFlags.putWithDefault(permissionName, newFlags, 0)
+ if (permissionFlags.isEmpty()) {
+ devicePermissionFlags -= deviceId
+ if (devicePermissionFlags.isEmpty()) {
+ appIdDevicePermissionFlags -= appId
+ }
+ }
+ }
+
+ companion object {
+ private const val DEVICE_ID_1 = "cdm:1"
+ private const val DEVICE_ID_2 = "cdm:2"
+ }
+}
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 820e44f..0fd424b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -334,6 +334,7 @@
final boolean dead = (behavior == ProcessBehavior.DEAD);
final ProcessRecord r = spy(new ProcessRecord(mAms, ai, processName, ai.uid));
+ r.mState = spy(r.mState);
r.setPid(mNextPid.getAndIncrement());
mActiveProcesses.add(r);
@@ -788,8 +789,8 @@
}) {
// Confirm expected OOM adjustments; we were invoked once to upgrade
// and once to downgrade
- assertEquals(String.valueOf(receiverApp), ActivityManager.PROCESS_STATE_RECEIVER,
- receiverApp.mState.getReportedProcState());
+ verify(receiverApp.mState, times(1).description(String.valueOf(receiverApp)))
+ .setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
verify(mAms, times(2)).enqueueOomAdjTargetLocked(eq(receiverApp));
if ((mImpl == Impl.DEFAULT) && (receiverApp == receiverBlueApp)) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 6906dec..76d4d55 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -18,7 +18,10 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.server.app.GameManagerService.CANCEL_GAME_LOADING_MODE;
+import static com.android.server.app.GameManagerService.Injector;
import static com.android.server.app.GameManagerService.LOADING_BOOST_MAX_DURATION;
+import static com.android.server.app.GameManagerService.PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED;
+import static com.android.server.app.GameManagerService.PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE;
import static com.android.server.app.GameManagerService.SET_GAME_STATE;
import static com.android.server.app.GameManagerService.WRITE_DELAY_MILLIS;
import static com.android.server.app.GameManagerService.WRITE_GAME_MODE_INTERVENTION_LIST_FILE;
@@ -33,6 +36,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
@@ -72,9 +76,12 @@
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.UserManager;
+import android.os.test.FakePermissionEnforcer;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
+import android.server.app.Flags;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -85,6 +92,7 @@
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;
@@ -108,6 +116,7 @@
@Presubmit
public class GameManagerServiceTests {
@Mock MockContext mMockContext;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TAG = "GameManagerServiceTests";
private static final String PACKAGE_NAME_INVALID = "com.android.app";
private static final int USER_ID_1 = 1001;
@@ -126,6 +135,11 @@
private UserManager mMockUserManager;
private BroadcastReceiver mShutDownActionReceiver;
+ private FakePermissionEnforcer mFakePermissionEnforcer = new FakePermissionEnforcer();
+
+ @Mock
+ private GameManagerServiceSystemPropertiesWrapper mSysPropsMock;
+
@Captor
ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
@@ -193,6 +207,8 @@
switch (name) {
case Context.USER_SERVICE:
return mMockUserManager;
+ case Context.PERMISSION_ENFORCER_SERVICE:
+ return mFakePermissionEnforcer;
}
throw new UnsupportedOperationException("Couldn't find system service: " + name);
}
@@ -222,6 +238,8 @@
when(mMockPackageManager.getPackageUidAsUser(mPackageName, USER_ID_1)).thenReturn(
DEFAULT_PACKAGE_UID);
LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_GAME_DEFAULT_FRAME_RATE);
}
private void mockAppCategory(String packageName, @ApplicationInfo.Category int category)
@@ -1695,9 +1713,8 @@
mockModifyGameModeGranted();
final Context context = InstrumentationRegistry.getContext();
GameManagerService gameManagerService =
- new GameManagerService(mMockContext,
- mTestLooper.getLooper(),
- context.getFilesDir());
+ new GameManagerService(mMockContext, mTestLooper.getLooper(), context.getFilesDir(),
+ new Injector());
startUser(gameManagerService, USER_ID_1);
startUser(gameManagerService, USER_ID_2);
@@ -1786,7 +1803,7 @@
mockDeviceConfigBattery();
final Context context = InstrumentationRegistry.getContext();
GameManagerService gameManagerService = new GameManagerService(mMockContext,
- mTestLooper.getLooper(), context.getFilesDir());
+ mTestLooper.getLooper(), context.getFilesDir(), new Injector());
startUser(gameManagerService, USER_ID_1);
startUser(gameManagerService, USER_ID_2);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
@@ -1962,7 +1979,7 @@
assertTrue(
gameManagerService.mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
USER_ID_1));
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(60.0f));
checkFps(gameManagerService, GameManager.GAME_MODE_CUSTOM, 60);
@@ -2035,7 +2052,7 @@
mTestLooper.getLooper()));
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(90.0f));
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
@@ -2044,7 +2061,7 @@
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configStringAfter);
gameManagerService.updateConfigsForUser(USER_ID_1, false, mPackageName);
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(0.0f));
}
@@ -2061,14 +2078,14 @@
mTestLooper.getLooper()));
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(90.0f));
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
mockInterventionsDisabledNoOptInFromXml();
gameManagerService.updateConfigsForUser(USER_ID_1, false, mPackageName);
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(0.0f));
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0);
@@ -2087,14 +2104,14 @@
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(90.0f));
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
mockInterventionsEnabledAllOptInFromXml();
gameManagerService.updateConfigsForUser(USER_ID_1, false, mPackageName);
- Mockito.verify(gameManagerService).setOverrideFrameRate(
+ Mockito.verify(gameManagerService).setGameModeFrameRateOverride(
ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
ArgumentMatchers.eq(0.0f));
}
@@ -2390,4 +2407,138 @@
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, false);
}
+
+ @Test
+ public void testGameDefaultFrameRate_FlagOn() throws Exception {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_GAME_MODE);
+
+ GameManagerService gameManagerService = Mockito.spy(
+ new GameManagerService(mMockContext, mTestLooper.getLooper(),
+ InstrumentationRegistry.getContext().getFilesDir(),
+ new Injector(){
+ @Override
+ public GameManagerServiceSystemPropertiesWrapper
+ createSystemPropertiesWrapper() {
+ return mSysPropsMock;
+ }
+ }));
+
+ // Set up a game in the foreground.
+ String[] packages = {mPackageName};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+
+ // Toggle game default frame rate on.
+ when(mSysPropsMock.getInt(
+ ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE),
+ anyInt())).thenReturn(60);
+ when(mSysPropsMock.getBoolean(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq(true))).thenReturn(true);
+ gameManagerService.toggleGameDefaultFrameRate(true);
+
+ // Verify that:
+ // 1) The system property is set correctly
+ // 2) setDefaultFrameRateOverride is called with correct arguments
+ Mockito.verify(mSysPropsMock).set(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq("true"));
+ Mockito.verify(gameManagerService, times(1))
+ .setGameDefaultFrameRateOverride(ArgumentMatchers.eq(DEFAULT_PACKAGE_UID),
+ ArgumentMatchers.eq(60.0f));
+
+ // Adding another game to the foreground.
+ String anotherGamePkg = "another.game";
+ String[] packages2 = {anotherGamePkg};
+ mockAppCategory(anotherGamePkg, ApplicationInfo.CATEGORY_GAME);
+ int somePackageId = DEFAULT_PACKAGE_UID + 1;
+ when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+
+ // Toggle game default frame rate off.
+ when(mSysPropsMock.getBoolean(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq(true))).thenReturn(false);
+ gameManagerService.toggleGameDefaultFrameRate(false);
+
+ // Verify that:
+ // 1) The system property is set correctly
+ // 2) setDefaultFrameRateOverride is called with correct arguments
+ Mockito.verify(mSysPropsMock).set(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq("false"));
+ Mockito.verify(gameManagerService).setGameDefaultFrameRateOverride(
+ ArgumentMatchers.eq(DEFAULT_PACKAGE_UID), ArgumentMatchers.eq(0.0f));
+ Mockito.verify(gameManagerService).setGameDefaultFrameRateOverride(
+ ArgumentMatchers.eq(somePackageId), ArgumentMatchers.eq(0.0f));
+ }
+
+ @Test
+ public void testGameDefaultFrameRate_FlagOff() throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_GAME_DEFAULT_FRAME_RATE);
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_GAME_MODE);
+
+ GameManagerService gameManagerService = Mockito.spy(
+ new GameManagerService(mMockContext, mTestLooper.getLooper(),
+ InstrumentationRegistry.getContext().getFilesDir(),
+ new Injector(){
+ @Override
+ public GameManagerServiceSystemPropertiesWrapper
+ createSystemPropertiesWrapper() {
+ return mSysPropsMock;
+ }
+ }));
+
+ // Set up a game in the foreground.
+ String[] packages = {mPackageName};
+ when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+
+ // Toggle game default frame rate on.
+ when(mSysPropsMock.getInt(
+ ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE),
+ anyInt())).thenReturn(60);
+ when(mSysPropsMock.getBoolean(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq(true))).thenReturn(true);
+
+ gameManagerService.toggleGameDefaultFrameRate(true);
+
+ // Verify that:
+ // 1) System property is never set
+ // 2) setGameDefaultFrameRateOverride() should never be called if the flag is disabled.
+ Mockito.verify(mSysPropsMock, never()).set(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ anyString());
+ Mockito.verify(gameManagerService, never())
+ .setGameDefaultFrameRateOverride(anyInt(), anyFloat());
+
+ // Toggle game default frame rate off.
+ String anotherGamePkg = "another.game";
+ String[] packages2 = {anotherGamePkg};
+ mockAppCategory(anotherGamePkg, ApplicationInfo.CATEGORY_GAME);
+ int somePackageId = DEFAULT_PACKAGE_UID + 1;
+ when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ when(mSysPropsMock.getBoolean(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ ArgumentMatchers.eq(true))).thenReturn(false);
+
+ gameManagerService.toggleGameDefaultFrameRate(false);
+ // Verify that:
+ // 1) System property is never set
+ // 2) setGameDefaultFrameRateOverride() should never be called if the flag is disabled.
+ Mockito.verify(mSysPropsMock, never()).set(
+ ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED),
+ anyString());
+ Mockito.verify(gameManagerService, never())
+ .setGameDefaultFrameRateOverride(anyInt(), anyFloat());
+ }
}
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 07197b1..05e0e8f 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -3,6 +3,20 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "power_stats_ravenwood_tests",
+ srcs: [
+ "src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java",
+ "src/com/android/server/power/stats/AggregatedPowerStatsTest.java",
+ "src/com/android/server/power/stats/MultiStateStatsTest.java",
+ "src/com/android/server/power/stats/PowerStatsAggregatorTest.java",
+ "src/com/android/server/power/stats/PowerStatsCollectorTest.java",
+ "src/com/android/server/power/stats/PowerStatsSchedulerTest.java",
+ "src/com/android/server/power/stats/PowerStatsStoreTest.java",
+ "src/com/android/server/power/stats/PowerStatsUidResolverTest.java",
+ ],
+}
+
android_test {
name: "PowerStatsTests",
@@ -12,8 +26,7 @@
],
exclude_srcs: [
- "src/com/android/server/power/stats/MultiStateStatsTest.java",
- "src/com/android/server/power/stats/PowerStatsStoreTest.java",
+ ":power_stats_ravenwood_tests",
],
static_libs: [
@@ -65,10 +78,12 @@
"modules-utils-binary-xml",
"androidx.annotation_annotation",
"androidx.test.rules",
+ "truth",
+ "mockito_ravenwood",
],
srcs: [
- "src/com/android/server/power/stats/MultiStateStatsTest.java",
- "src/com/android/server/power/stats/PowerStatsStoreTest.java",
+ ":power_stats_ravenwood_tests",
+ "src/com/android/server/power/stats/MockClock.java",
],
auto_gen_config: true,
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
similarity index 99%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
index 6d61dc8..af83be0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
@@ -35,7 +35,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class AggregatePowerStatsProcessorTest {
+public class AggregatedPowerStatsProcessorTest {
@Test
public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index e8f46b3..1b045c5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -22,12 +22,10 @@
import static org.junit.Assert.assertThrows;
import android.os.BatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,11 +36,6 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MultiStateStatsTest {
-
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .build();
-
public static final int DIMENSION_COUNT = 2;
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index 6704987..2456636 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -24,7 +24,6 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
-import android.text.format.DateFormat;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
@@ -39,7 +38,8 @@
import org.junit.runner.RunWith;
import java.text.ParseException;
-import java.util.Calendar;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.List;
import java.util.TimeZone;
@@ -60,7 +60,7 @@
public void setup() throws ParseException {
mHistory = new BatteryStatsHistory(32, 1024,
mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
- mMonotonicClock);
+ mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class));
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(TEST_POWER_COMPONENT)
@@ -179,9 +179,9 @@
@NonNull
private static CharSequence formatDateTime(long timeInMillis) {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
- cal.setTimeInMillis(timeInMillis);
- return DateFormat.format("yyyy-MM-dd hh:mm:ss", cal);
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+ format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
+ return format.format(new Date(timeInMillis));
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index 330f698..17a7d3e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PersistableBundle;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +30,18 @@
import com.android.internal.os.PowerStats;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PowerStatsCollectorTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
private Handler mHandler;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index 7257a94..beec661 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -24,26 +24,26 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.content.Context;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
+import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerProfile;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -51,39 +51,46 @@
@RunWith(AndroidJUnit4.class)
public class PowerStatsSchedulerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private PowerStatsStore mPowerStatsStore;
private Handler mHandler;
private MockClock mClock = new MockClock();
private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
- private MockBatteryStatsImpl mBatteryStats;
private PowerStatsScheduler mPowerStatsScheduler;
- private PowerProfile mPowerProfile;
private PowerStatsAggregator mPowerStatsAggregator;
private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+ private List<Long> mScheduledAlarms = new ArrayList<>();
+ private boolean mPowerStatsCollectionOccurred;
+
+ private static final int START_REALTIME = 7654321;
@Before
@SuppressWarnings("GuardedBy")
- public void setup() {
- final Context context = InstrumentationRegistry.getContext();
-
+ public void setup() throws IOException {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
- mClock.realtime = 7654321;
+ mClock.realtime = START_REALTIME;
HandlerThread bgThread = new HandlerThread("bg thread");
bgThread.start();
- File systemDir = context.getCacheDir();
mHandler = new Handler(bgThread.getLooper());
mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
- mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig);
- mPowerProfile = mock(PowerProfile.class);
- when(mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)).thenReturn(1000000.0);
- mBatteryStats = new MockBatteryStatsImpl(mClock).setPowerProfile(mPowerProfile);
+ mPowerStatsStore = new PowerStatsStore(
+ Files.createTempDirectory("PowerStatsSchedulerTest").toFile(),
+ mHandler, mAggregatedPowerStatsConfig);
mPowerStatsAggregator = mock(PowerStatsAggregator.class);
- mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
- TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), mPowerStatsStore, mClock,
- mMonotonicClock, mHandler, mBatteryStats);
+ mPowerStatsScheduler = new PowerStatsScheduler(
+ () -> mPowerStatsCollectionOccurred = true,
+ mPowerStatsAggregator, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1),
+ mPowerStatsStore,
+ ((triggerAtMillis, tag, onAlarmListener, handler) ->
+ mScheduledAlarms.add(triggerAtMillis)),
+ mClock, mMonotonicClock, () -> 12345L, mHandler);
}
@Test
@@ -113,7 +120,7 @@
long endTimeWallClock =
mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
- assertThat(startTime).isEqualTo(7654321 + 123);
+ assertThat(startTime).isEqualTo(START_REALTIME + 123);
assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
assertThat(Instant.ofEpochMilli(endTimeWallClock))
.isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
@@ -142,11 +149,15 @@
}).when(mPowerStatsAggregator).aggregatePowerStats(anyLong(), anyLong(),
any(Consumer.class));
- mPowerStatsScheduler.schedulePowerStatsAggregation();
+ mPowerStatsScheduler.start(/*enabled*/ true);
ConditionVariable done = new ConditionVariable();
mHandler.post(done::open);
done.block();
+ assertThat(mPowerStatsCollectionOccurred).isTrue();
+ assertThat(mScheduledAlarms).containsExactly(
+ START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1));
+
verify(mPowerStatsAggregator, times(2))
.aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class));
@@ -155,7 +166,7 @@
// Skip the first entry, which was placed in the store at the beginning of this test
PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
- assertThat(timeFrame1.startMonotonicTime).isEqualTo(7654321 + 123);
+ assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123);
assertThat(timeFrame2.startMonotonicTime)
.isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 8bb11a4..36d55a4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -209,6 +209,7 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.permission.PermissionManager;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
@@ -971,6 +972,12 @@
return new NotificationRecord(mContext, sbn, channel);
}
+ private NotificationRecord generateNotificationRecord(NotificationChannel channel,
+ long postTime) {
+ final StatusBarNotification sbn = generateSbn(PKG, mUid, postTime, 0);
+ return new NotificationRecord(mContext, sbn, channel);
+ }
+
private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) {
return generateNotificationRecord(channel, 1, userId);
}
@@ -8978,6 +8985,42 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void testAddAutomaticZenRule_typeManagedCanBeUsedByDeviceOwners() throws Exception {
+ mService.setCallerIsNormalPackage();
+ mService.setZenHelper(mock(ZenModeHelper.class));
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+ .setType(AutomaticZenRule.TYPE_MANAGED)
+ .setOwner(new ComponentName("pkg", "cls"))
+ .build();
+ when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(true);
+
+ mBinderService.addAutomaticZenRule(rule, "pkg");
+ // No exception!
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void testAddAutomaticZenRule_typeManagedCannotBeUsedByRegularApps() throws Exception {
+ mService.setCallerIsNormalPackage();
+ mService.setZenHelper(mock(ZenModeHelper.class));
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+ .setType(AutomaticZenRule.TYPE_MANAGED)
+ .setOwner(new ComponentName("pkg", "cls"))
+ .build();
+ when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(false);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mBinderService.addAutomaticZenRule(rule, "pkg"));
+ }
+
+ @Test
public void onZenModeChanged_sendsBroadcasts() throws Exception {
when(mAmi.getCurrentUserId()).thenReturn(100);
when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102});
@@ -13312,6 +13355,175 @@
assertThat(n.flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
}
+ @Test
+ public void cancelNotificationsFromListener_rapidClear_oldNew_cancelOne()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create recent notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis());
+ mService.addNotification(nr1);
+
+ // Create old notification.
+ final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr2);
+
+ // Cancel specific notifications via listener.
+ String[] keys = {nr1.getSbn().getKey(), nr2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+
+ // Notifications should not be active anymore.
+ StatusBarNotification[] notifications = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifications).isEmpty();
+ assertEquals(0, mService.getNotificationRecordCount());
+ // Ensure cancel event is logged.
+ verify(mAppOpsManager).noteOpNoThrow(
+ AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER, mUid, PKG, null, null);
+ }
+
+ @Test
+ public void cancelNotificationsFromListener_rapidClear_old_cancelOne() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create old notifications.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr1);
+
+ final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr2);
+
+ // Cancel specific notifications via listener.
+ String[] keys = {nr1.getSbn().getKey(), nr2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+
+ // Notifications should not be active anymore.
+ StatusBarNotification[] notifications = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifications).isEmpty();
+ assertEquals(0, mService.getNotificationRecordCount());
+ // Ensure cancel event is not logged.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void cancelNotificationsFromListener_rapidClear_oldNew_cancelOne_flagDisabled()
+ throws RemoteException {
+ mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create recent notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis());
+ mService.addNotification(nr1);
+
+ // Create old notification.
+ final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr2);
+
+ // Cancel specific notifications via listener.
+ String[] keys = {nr1.getSbn().getKey(), nr2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+
+ // Notifications should not be active anymore.
+ StatusBarNotification[] notifications = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifications).isEmpty();
+ assertEquals(0, mService.getNotificationRecordCount());
+ // Ensure cancel event is not logged due to flag being disabled.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void cancelNotificationsFromListener_rapidClear_oldNew_cancelAll()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create recent notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis());
+ mService.addNotification(nr1);
+
+ // Create old notification.
+ final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr2);
+
+ // Cancel all notifications via listener.
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+
+ // Notifications should not be active anymore.
+ StatusBarNotification[] notifications = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifications).isEmpty();
+ assertEquals(0, mService.getNotificationRecordCount());
+ // Ensure cancel event is logged.
+ verify(mAppOpsManager).noteOpNoThrow(
+ AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER, mUid, PKG, null, null);
+ }
+
+ @Test
+ public void cancelNotificationsFromListener_rapidClear_old_cancelAll() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create old notifications.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr1);
+
+ final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr2);
+
+ // Cancel all notifications via listener.
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+
+ // Notifications should not be active anymore.
+ StatusBarNotification[] notifications = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifications).isEmpty();
+ assertEquals(0, mService.getNotificationRecordCount());
+ // Ensure cancel event is not logged.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void cancelNotificationsFromListener_rapidClear_oldNew_cancelAll_flagDisabled()
+ throws RemoteException {
+ mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
+ .FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
+
+ // Create recent notification.
+ final NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
+ System.currentTimeMillis());
+ mService.addNotification(nr1);
+
+ // Create old notification.
+ final NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, 0);
+ mService.addNotification(nr2);
+
+ // Cancel all notifications via listener.
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+
+ // Notifications should not be active anymore.
+ StatusBarNotification[] notifications = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifications).isEmpty();
+ assertEquals(0, mService.getNotificationRecordCount());
+ // Ensure cancel event is not logged due to flag being disabled.
+ verify(mAppOpsManager, never()).noteOpNoThrow(
+ eq(AppOpsManager.OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER), anyInt(), anyString(),
+ any(), any());
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
new file mode 100644
index 0000000..49efd1b
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class VibratorControlServiceTest {
+
+ private VibratorControlService mVibratorControlService;
+ private final Object mLock = new Object();
+
+ @Before
+ public void setUp() throws Exception {
+ mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
+ }
+
+ @Test
+ public void testRegisterVibratorController() throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+
+ assertThat(fakeController.isLinkedToDeath).isTrue();
+ }
+
+ @Test
+ public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+ mVibratorControlService.unregisterVibratorController(fakeController);
+ assertThat(fakeController.isLinkedToDeath).isFalse();
+ }
+
+ @Test
+ public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController1 = new FakeVibratorController();
+ FakeVibratorController fakeController2 = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController1);
+
+ mVibratorControlService.unregisterVibratorController(fakeController2);
+ assertThat(fakeController1.isLinkedToDeath).isTrue();
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
new file mode 100644
index 0000000..79abe21
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class VibratorControllerHolderTest {
+
+ private final FakeVibratorController mFakeVibratorController = new FakeVibratorController();
+ private VibratorControllerHolder mVibratorControllerHolder;
+
+ @Before
+ public void setUp() throws Exception {
+ mVibratorControllerHolder = new VibratorControllerHolder();
+ }
+
+ @Test
+ public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ assertThat(mVibratorControllerHolder.getVibratorController())
+ .isEqualTo(mFakeVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
+ }
+
+ @Test
+ public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ mVibratorControllerHolder.setVibratorController(null);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
+ assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
+ }
+
+ @Test
+ public void testBinderDied_withValidController_unlinksVibratorControllerToDeath()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ mVibratorControllerHolder.binderDied(mFakeVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isFalse();
+ assertThat(mVibratorControllerHolder.getVibratorController()).isNull();
+ }
+
+ @Test
+ public void testBinderDied_withInvalidController_ignoresRequest()
+ throws RemoteException {
+ mVibratorControllerHolder.setVibratorController(mFakeVibratorController);
+ FakeVibratorController imposterVibratorController = new FakeVibratorController();
+ mVibratorControllerHolder.binderDied(imposterVibratorController);
+ assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
+ assertThat(mVibratorControllerHolder.getVibratorController())
+ .isEqualTo(mFakeVibratorController);
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 4e9bbe0..d6b2116 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -307,9 +307,10 @@
@Override
void addService(String name, IBinder service) {
- Object serviceInstance = service;
- mExternalVibratorService =
- (VibratorManagerService.ExternalVibratorService) serviceInstance;
+ if (service instanceof VibratorManagerService.ExternalVibratorService) {
+ mExternalVibratorService =
+ (VibratorManagerService.ExternalVibratorService) service;
+ }
}
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
new file mode 100644
index 0000000..7e23587
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 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.vibrator;
+
+import android.annotation.NonNull;
+import android.frameworks.vibrator.IVibratorController;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for
+ * testing.
+ */
+public final class FakeVibratorController extends IVibratorController.Stub {
+
+ public boolean isLinkedToDeath = false;
+
+ @Override
+ public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException {
+
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ super.linkToDeath(recipient, flags);
+ isLinkedToDeath = true;
+ }
+
+ @Override
+ public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ isLinkedToDeath = false;
+ return super.unlinkToDeath(recipient, flags);
+ }
+}
diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp
index 744cb63..8a79fe4 100644
--- a/services/tests/voiceinteractiontests/Android.bp
+++ b/services/tests/voiceinteractiontests/Android.bp
@@ -44,6 +44,7 @@
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
"truth",
+ "frameworks-base-testutils",
],
libs: [
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
new file mode 100644
index 0000000..656957c
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.voiceinteraction;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.PermissionEnforcer;
+import android.os.Process;
+import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+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.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SetSandboxedTrainingDataAllowedTest {
+
+ @Captor private ArgumentCaptor<Integer> mOpIdCaptor, mUidCaptor, mOpModeCaptor;
+
+ @Mock
+ private AppOpsManager mAppOpsManager;
+
+ @Mock
+ private VoiceInteractionManagerServiceImpl mVoiceInteractionManagerServiceImpl;
+
+ private FakePermissionEnforcer mPermissionEnforcer = new FakePermissionEnforcer();
+
+ private Context mContext;
+
+ private VoiceInteractionManagerService mVoiceInteractionManagerService;
+ private VoiceInteractionManagerService.VoiceInteractionManagerServiceStub
+ mVoiceInteractionManagerServiceStub;
+
+ private ApplicationInfo mApplicationInfo = new ApplicationInfo();
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.WARN)
+ .mockStatic(LocalServices.class)
+ .mockStatic(PermissionEnforcer.class)
+ .build();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+
+ doReturn(mPermissionEnforcer).when(() -> PermissionEnforcer.fromContext(any()));
+ doReturn(mock(PermissionManagerServiceInternal.class)).when(
+ () -> LocalServices.getService(PermissionManagerServiceInternal.class));
+ doReturn(mock(ActivityManagerInternal.class)).when(
+ () -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(UserManagerInternal.class)).when(
+ () -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mock(ActivityTaskManagerInternal.class)).when(
+ () -> LocalServices.getService(ActivityTaskManagerInternal.class));
+ doReturn(mock(LegacyPermissionManagerInternal.class)).when(
+ () -> LocalServices.getService(LegacyPermissionManagerInternal.class));
+ doReturn(mock(RoleManager.class)).when(mContext).getSystemService(RoleManager.class);
+ doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
+ doReturn(mApplicationInfo).when(mVoiceInteractionManagerServiceImpl).getApplicationInfo();
+
+ mVoiceInteractionManagerService = new VoiceInteractionManagerService(mContext);
+ mVoiceInteractionManagerServiceStub =
+ mVoiceInteractionManagerService.new VoiceInteractionManagerServiceStub();
+ mVoiceInteractionManagerServiceStub.mImpl = mVoiceInteractionManagerServiceImpl;
+ mPermissionEnforcer.grant(Manifest.permission.MANAGE_HOTWORD_DETECTION);
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_currentAndPreinstalledAssistant_setsOp() {
+ // Set application info so current app is the current and preinstalled assistant.
+ mApplicationInfo.uid = Process.myUid();
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true);
+
+ verify(mAppOpsManager).setUidMode(mOpIdCaptor.capture(), mUidCaptor.capture(),
+ mOpModeCaptor.capture());
+ assertThat(mOpIdCaptor.getValue()).isEqualTo(
+ AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA);
+ assertThat(mOpModeCaptor.getValue()).isEqualTo(AppOpsManager.MODE_ALLOWED);
+ assertThat(mUidCaptor.getValue()).isEqualTo(Process.myUid());
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_missingPermission_doesNotSetOp() {
+ // Set application info so current app is the current and preinstalled assistant.
+ mApplicationInfo.uid = Process.myUid();
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ // Simulate missing MANAGE_HOTWORD_DETECTION permission.
+ mPermissionEnforcer.revoke(Manifest.permission.MANAGE_HOTWORD_DETECTION);
+
+ assertThrows(SecurityException.class,
+ () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true));
+
+ verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_notPreinstalledAssistant_doesNotSetOp() {
+ // Set application info so current app is not preinstalled assistant.
+ mApplicationInfo.uid = Process.myUid();
+ mApplicationInfo.flags = ApplicationInfo.FLAG_INSTALLED; // Does not contain FLAG_SYSTEM.
+
+ assertThrows(SecurityException.class,
+ () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true));
+
+ verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void setIsReceiveSandboxedTrainingDataAllowed_notCurrentAssistant_doesNotSetOp() {
+ // Set application info so current app is not current assistant.
+ mApplicationInfo.uid = Process.SHELL_UID; // Set current assistant uid to shell UID.
+ mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+ assertThrows(SecurityException.class,
+ () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed(
+ /* allowed= */ true));
+
+ verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 71d2504..dfe79bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -993,7 +993,9 @@
dc.getDisplayPolicy().getDecorInsetsInfo(ROTATION_0, dc.mBaseDisplayHeight,
dc.mBaseDisplayWidth).mConfigFrame.set(0, 0, 1000, 990);
dc.computeScreenConfiguration(config, ROTATION_0);
+ dc.onRequestedOverrideConfigurationChanged(config);
assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation);
+ assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalOrientation());
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index b214591..6e4f13a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2273,9 +2273,9 @@
private boolean isCallerPreinstalledAssistant() {
return mImpl != null
- && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid()
- && (mImpl.mInfo.getServiceInfo().applicationInfo.isSystemApp()
- || mImpl.mInfo.getServiceInfo().applicationInfo.isUpdatedSystemApp());
+ && mImpl.getApplicationInfo().uid == Binder.getCallingUid()
+ && (mImpl.getApplicationInfo().isSystemApp()
+ || mImpl.getApplicationInfo().isUpdatedSystemApp());
}
private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 3c4b58f..7e0cbad 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -40,6 +40,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
@@ -540,6 +541,10 @@
return mInfo.getSupportsLocalInteraction();
}
+ public ApplicationInfo getApplicationInfo() {
+ return mInfo.getServiceInfo().applicationInfo;
+ }
+
public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token);
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index a004cc3..2506360 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.WorkerThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.text.Editable;
@@ -39,6 +40,9 @@
* </ul>
* <p>
* The formatting will be restarted once the text is cleared.
+ *
+ * @deprecated This is a thin wrapper on a `libphonenumber` `AsYouTypeFormatter`; it is recommended
+ * to use that instead.
*/
public class PhoneNumberFormattingTextWatcher implements TextWatcher {
@@ -69,6 +73,7 @@
* @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
* where the phone number is being entered.
*/
+ @WorkerThread
public PhoneNumberFormattingTextWatcher(String countryCode) {
if (countryCode == null) throw new IllegalArgumentException();
mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b356fde..326b6f5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1508,8 +1508,14 @@
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
if (listener == null) return;
- addOnSubscriptionsChangedListener(
- new HandlerExecutor(new Handler(listener.getCreatorLooper())), listener);
+ Looper looper = listener.getCreatorLooper();
+ if (looper == null) {
+ throw new RuntimeException(
+ "Can't create handler inside thread " + Thread.currentThread()
+ + " that has not called Looper.prepare()");
+ }
+
+ addOnSubscriptionsChangedListener(new HandlerExecutor(new Handler(looper)), listener);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b96914e..f206987 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -18009,6 +18009,64 @@
}
/**
+ * Enable or disable notifications sent for cellular identifier disclosure events.
+ *
+ * Disclosure events are defined as instances where a device has sent a cellular identifier
+ * on the Non-access stratum (NAS) before a security context is established. As a result the
+ * identifier is sent in the clear, which has privacy implications for the user.
+ *
+ * @param enable if notifications about disclosure events should be enabled
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support this feature.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY)
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public void enableCellularIdentifierDisclosureNotifications(boolean enable) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.enableCellularIdentifierDisclosureNotifications(enable);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "enableCellularIdentifierDisclosureNotifications RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get whether or not cellular identifier disclosure notifications are enabled.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support this feature.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public boolean isCellularIdentifierDisclosureNotificationEnabled() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isCellularIdentifierDisclosureNotificationEnabled();
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "isCellularIdentifierDisclosureNotificationEnabled RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
* Get current cell broadcast message identifier ranges.
*
* @throws SecurityException if the caller does not have the required permission
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4c53f8a..d7d28a1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3159,4 +3159,37 @@
* @return {@code true} if the operation is successful, {@code false} otherwise.
*/
boolean setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode);
+
+ /**
+ * Enable or disable notifications sent for cellular identifier disclosure events.
+ *
+ * Disclosure events are defined as instances where a device has sent a cellular identifier
+ * on the Non-access stratum (NAS) before a security context is established. As a result the
+ * identifier is sent in the clear, which has privacy implications for the user.
+ *
+ * <p>Requires permission: android.Manifest.MODIFY_PHONE_STATE</p>
+ *
+ * @param enabled if notifications about disclosure events should be enabled
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support this feature.
+ * @hide
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.MODIFY_PHONE_STATE)")
+ void enableCellularIdentifierDisclosureNotifications(boolean enable);
+
+ /**
+ * Get whether or not cellular identifier disclosure notifications are enabled.
+ *
+ * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE</p>
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support this feature.
+ * @hide
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+ boolean isCellularIdentifierDisclosureNotificationEnabled();
}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
index 068dfe8..a135623 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
@@ -100,6 +100,10 @@
mLastStateChangeTimestampMs = timestampMs;
}
+ public void setValue(int state, long[] values) {
+ System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength);
+ }
+
public void updateValue(long[] values, long timestampMs) {
if (mEnabled || mLastUpdateTimestampMs < mLastStateChangeTimestampMs) {
if (timestampMs < mLastStateChangeTimestampMs) {
@@ -306,6 +310,11 @@
return getInstance(instanceId).mArrayLength;
}
+ public static void native_setValues(long instanceId, int state, long containerInstanceId) {
+ getInstance(instanceId).setValue(state,
+ LongArrayContainer_host.getInstance(containerInstanceId));
+ }
+
public static void native_updateValues(long instanceId, long containerInstanceId,
long timestampMs) {
getInstance(instanceId).updateValue(