summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt9
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java21
-rw-r--r--core/java/android/app/UiAutomationConnection.java1
-rw-r--r--core/java/android/attention/AttentionManagerInternal.java4
-rw-r--r--core/java/android/os/AggregateBatteryConsumer.java14
-rw-r--r--core/java/android/os/BatteryConsumer.java15
-rw-r--r--core/java/android/os/BatteryUsageStats.java7
-rw-r--r--core/java/android/os/UserManager.java5
-rw-r--r--core/java/android/provider/Settings.java33
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java57
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java77
-rw-r--r--core/java/android/view/View.java130
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/java/android/view/WindowManager.java31
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java57
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java35
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java25
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java24
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java7
-rw-r--r--core/java/android/widget/OWNERS2
-rw-r--r--core/java/android/widget/TextView.java7
-rw-r--r--core/java/android/window/WindowContainerTransaction.java30
-rw-r--r--core/proto/android/os/batteryusagestats.proto17
-rw-r--r--core/proto/android/os/processstarttime.proto92
-rw-r--r--core/res/res/layout/side_fps_toast.xml43
-rw-r--r--core/res/res/values/attrs.xml14
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/public-staging.xml1
-rw-r--r--core/res/res/values/strings.xml14
-rw-r--r--core/res/res/values/symbols.xml9
-rw-r--r--core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java50
-rw-r--r--core/tests/coretests/src/android/view/WindowParamsTest.java100
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java10
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SystemUI/TEST_MAPPING16
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java92
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt189
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java202
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt60
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java25
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java1
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java69
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java2
-rw-r--r--services/core/java/com/android/server/am/HostingRecord.java97
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java9
-rw-r--r--services/core/java/com/android/server/audio/RecordingActivityMonitor.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/policy/SideFpsEventHandler.java164
-rw-r--r--services/core/java/com/android/server/policy/SideFpsToast.java69
-rw-r--r--services/core/java/com/android/server/wm/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java6
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java82
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java16
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java21
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java30
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java30
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp18
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp48
92 files changed, 2133 insertions, 638 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 7ed1a4382054..ea2ae0c9e15f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -275,6 +275,7 @@ package android {
public static final class R.attr {
ctor public R.attr();
field public static final int absListViewStyle = 16842858; // 0x101006a
+ field public static final int accessibilityDataPrivate;
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
field public static final int accessibilityFlags = 16843652; // 0x1010384
@@ -49955,6 +49956,7 @@ package android.view {
method public void invalidate();
method public void invalidateDrawable(@NonNull android.graphics.drawable.Drawable);
method public void invalidateOutline();
+ method public boolean isAccessibilityDataPrivate();
method public boolean isAccessibilityFocused();
method public boolean isAccessibilityHeading();
method public boolean isActivated();
@@ -50132,6 +50134,7 @@ package android.view {
method public void scrollTo(int, int);
method public void sendAccessibilityEvent(int);
method public void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent);
+ method public void setAccessibilityDataPrivate(int);
method public void setAccessibilityDelegate(@Nullable android.view.View.AccessibilityDelegate);
method public void setAccessibilityHeading(boolean);
method public void setAccessibilityLiveRegion(int);
@@ -50312,6 +50315,9 @@ package android.view {
method @CallSuper protected boolean verifyDrawable(@NonNull android.graphics.drawable.Drawable);
method @Deprecated public boolean willNotCacheDrawing();
method public boolean willNotDraw();
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_AUTO = 0; // 0x0
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_NO = 2; // 0x2
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_YES = 1; // 0x1
field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
@@ -51764,9 +51770,11 @@ package android.view.accessibility {
method public int getSpeechStateChangeTypes();
method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
+ method public boolean isAccessibilityDataPrivate();
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain();
+ method public void setAccessibilityDataPrivate(boolean);
method public void setAction(int);
method public void setContentChangeTypes(int);
method public void setEventTime(long);
@@ -51857,6 +51865,7 @@ package android.view.accessibility {
method public static boolean isAccessibilityButtonSupported();
method public boolean isAudioDescriptionRequested();
method public boolean isEnabled();
+ method public boolean isRequestFromAccessibilityTool();
method public boolean isTouchExplorationEnabled();
method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
method public boolean removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ddbccfa8f404..6c941e751b9e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -90,6 +90,7 @@ package android.accessibilityservice {
public class AccessibilityServiceInfo implements android.os.Parcelable {
method @NonNull public android.content.ComponentName getComponentName();
+ method public void setAccessibilityTool(boolean);
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 2e89ce83cd36..8f6bfd3b13db 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -784,6 +784,7 @@ public class AccessibilityServiceInfo implements Parcelable {
mNonInteractiveUiTimeout = other.mNonInteractiveUiTimeout;
mInteractiveUiTimeout = other.mInteractiveUiTimeout;
flags = other.flags;
+ mIsAccessibilityTool = other.mIsAccessibilityTool;
}
private boolean isRequestAccessibilityButtonChangeEnabled(IPlatformCompat platformCompat) {
@@ -1112,6 +1113,26 @@ public class AccessibilityServiceInfo implements Parcelable {
}
/**
+ * Sets whether the service is used to assist users with disabilities.
+ *
+ * <p>
+ * This property is normally provided in the service's {@link #mResolveInfo ResolveInfo}.
+ * </p>
+ *
+ * <p>
+ * This method is helpful for unit testing. However, this property is not dynamically
+ * configurable by a standard {@link AccessibilityService} so it's not possible to update the
+ * copy held by the system with this method.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ public void setAccessibilityTool(boolean isAccessibilityTool) {
+ mIsAccessibilityTool = isAccessibilityTool;
+ }
+
+ /**
* Indicates if the service is used to assist users with disabilities.
*
* @return {@code true} if the property is set to true.
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index a045157e02db..8d57e32a763c 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -550,6 +550,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
+ info.setAccessibilityTool(true);
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
index 47bec618cfd9..24fe0dbe5760 100644
--- a/core/java/android/attention/AttentionManagerInternal.java
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -83,11 +83,11 @@ public abstract class AttentionManagerInternal {
}
/** Internal interface for proximity callback. */
- public abstract static class ProximityUpdateCallbackInternal {
+ public interface ProximityUpdateCallbackInternal {
/**
* @param distance the estimated distance of the user (in meter)
* The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
*/
- public abstract void onProximityUpdate(double distance);
+ void onProximityUpdate(double distance);
}
}
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index e088f306cd98..068df22e222b 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -19,6 +19,7 @@ package android.os;
import android.annotation.NonNull;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
+import android.util.proto.ProtoOutputStream;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -97,6 +98,19 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {
}
}
+ void writePowerComponentModelProto(@NonNull ProtoOutputStream proto) {
+ for (int i = 0; i < POWER_COMPONENT_COUNT; i++) {
+ final int powerModel = getPowerModel(i);
+ if (powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) continue;
+
+ final long token = proto.start(BatteryUsageStatsAtomsProto.COMPONENT_MODELS);
+ proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.COMPONENT, i);
+ proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_MODEL,
+ powerModelToProtoEnum(powerModel));
+ proto.end(token);
+ }
+ }
+
/**
* Builder for DeviceBatteryConsumer.
*/
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index f4ca1b50fe86..de2dec779c99 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -479,6 +479,21 @@ public abstract class BatteryConsumer {
}
/**
+ * Returns the equivalent PowerModel enum for the specified power model.
+ * {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel}
+ */
+ public static int powerModelToProtoEnum(@BatteryConsumer.PowerModel int powerModel) {
+ switch (powerModel) {
+ case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+ return BatteryUsageStatsAtomsProto.PowerComponentModel.MEASURED_ENERGY;
+ case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
+ return BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_PROFILE;
+ default:
+ return BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED;
+ }
+ }
+
+ /**
* Returns the name of the specified process state. Intended for logging and debugging.
*/
public static String processStateToString(@BatteryConsumer.ProcessState int processState) {
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index b76592d4be58..7105b1ad8b38 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -276,7 +276,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
/**
* Returns a battery consumer for the specified battery consumer type.
*/
- public BatteryConsumer getAggregateBatteryConsumer(
+ public AggregateBatteryConsumer getAggregateBatteryConsumer(
@AggregateBatteryConsumerScope int scope) {
return mAggregateBatteryConsumers[scope];
}
@@ -450,7 +450,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
@NonNull
private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) {
- final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
+ final AggregateBatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
@@ -462,6 +462,9 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
getDischargeDurationMs());
deviceBatteryConsumer.writeStatsProto(proto,
BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
+ if (mIncludesPowerModels) {
+ deviceBatteryConsumer.writePowerComponentModelProto(proto);
+ }
writeUidBatteryConsumersProto(proto, maxRawSize);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c802e56c8dc7..58162f6b5abe 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -90,6 +90,7 @@ import java.util.Set;
public class UserManager {
private static final String TAG = "UserManager";
+ private static final boolean VERBOSE = false;
@UnsupportedAppUsage
private final IUserManager mService;
@@ -2056,10 +2057,10 @@ public class UserManager {
final String emulatedMode = SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY);
switch (emulatedMode) {
case SYSTEM_USER_MODE_EMULATION_FULL:
- Log.d(TAG, "isHeadlessSystemUserMode(): emulating as false");
+ if (VERBOSE) Log.v(TAG, "isHeadlessSystemUserMode(): emulating as false");
return false;
case SYSTEM_USER_MODE_EMULATION_HEADLESS:
- Log.d(TAG, "isHeadlessSystemUserMode(): emulating as true");
+ if (VERBOSE) Log.v(TAG, "isHeadlessSystemUserMode(): emulating as true");
return true;
case SYSTEM_USER_MODE_EMULATION_DEFAULT:
case "": // property not set
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 07e48acef074..05b3925fcb9f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14264,7 +14264,7 @@ public final class Settings {
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE = 0;
+ public static final int DEFAULT_ENABLE_TARE = 1;
/**
* Whether to enable the TARE AlarmManager economic policy or not.
@@ -17762,6 +17762,37 @@ public final class Settings {
* @hide
*/
public static final String CHARGING_SOUNDS_ENABLED = "wear_charging_sounds_enabled";
+
+ /** The status of the early updates process.
+ * @hide
+ */
+ public static final String EARLY_UPDATES_STATUS = "early_updates_status";
+
+ /**
+ * Early updates not started
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_NOT_STARTED = 0;
+ /**
+ * Early updates started and in progress
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_STARTED = 1;
+ /**
+ * Early updates completed and was successful
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_SUCCESS = 2;
+ /**
+ * Early updates skipped
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_SKIPPED = 3;
+ /**
+ * Early updates aborted due to timeout
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_ABORTED = 4;
}
}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 72341453a1f4..ab71459ed51e 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -95,6 +95,16 @@ public final class HotwordDetectedResult implements Parcelable {
/** Limits the max value for the triggered audio channel. */
private static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE = 63;
+ /**
+ * The bundle key for proximity value
+ *
+ * TODO(b/238896013): Move the proximity logic out of bundle to proper API.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PROXIMITY_METERS =
+ "android.service.voice.extra.PROXIMITY_METERS";
+
/** Confidence level in the trigger outcome. */
@HotwordConfidenceLevelValue
private final int mConfidenceLevel;
@@ -197,6 +207,14 @@ public final class HotwordDetectedResult implements Parcelable {
* <p>The use of this method is discouraged, and support for it will be removed in future
* versions of Android.
*
+ * <p>After the trigger happens, a special case of proximity-related extra, with the key of
+ * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double),
+ * will be stored to enable proximity logic. The proximity meters is provided by the system,
+ * on devices that support detecting proximity of nearby users, to help disambiguate which
+ * nearby device should respond. When the proximity is unknown, the proximity value will not
+ * be stored. This mapping will be excluded from the max bundle size calculation because this
+ * mapping is included after the result is returned from the hotword detector service.
+ *
* <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents
* that can be used to communicate with other processes.
*/
@@ -315,8 +333,23 @@ public final class HotwordDetectedResult implements Parcelable {
"audioChannel");
}
if (!mExtras.isEmpty()) {
- Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(),
- "extras");
+ // Remove the proximity key from the bundle before checking the bundle size. The
+ // proximity value is added after the privileged module and can avoid the
+ // maxBundleSize limitation.
+ if (mExtras.containsKey(EXTRA_PROXIMITY_METERS)) {
+ double proximityMeters = mExtras.getDouble(EXTRA_PROXIMITY_METERS);
+ mExtras.remove(EXTRA_PROXIMITY_METERS);
+ // Skip checking parcelable size if the new bundle size is 0. Newly empty bundle
+ // has parcelable size of 4, but the default bundle has parcelable size of 0.
+ if (mExtras.size() > 0) {
+ Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0,
+ getMaxBundleSize(), "extras");
+ }
+ mExtras.putDouble(EXTRA_PROXIMITY_METERS, proximityMeters);
+ } else {
+ Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0,
+ getMaxBundleSize(), "extras");
+ }
}
}
@@ -513,6 +546,14 @@ public final class HotwordDetectedResult implements Parcelable {
* <p>The use of this method is discouraged, and support for it will be removed in future
* versions of Android.
*
+ * <p>After the trigger happens, a special case of proximity-related extra, with the key of
+ * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double),
+ * will be stored to enable proximity logic. The proximity meters is provided by the system,
+ * on devices that support detecting proximity of nearby users, to help disambiguate which
+ * nearby device should respond. When the proximity is unknown, the proximity value will not
+ * be stored. This mapping will be excluded from the max bundle size calculation because this
+ * mapping is included after the result is returned from the hotword detector service.
+ *
* <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents
* that can be used to communicate with other processes.
*/
@@ -813,6 +854,14 @@ public final class HotwordDetectedResult implements Parcelable {
* <p>The use of this method is discouraged, and support for it will be removed in future
* versions of Android.
*
+ * <p>After the trigger happens, a special case of proximity-related extra, with the key of
+ * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double),
+ * will be stored to enable proximity logic. The proximity meters is provided by the system,
+ * on devices that support detecting proximity of nearby users, to help disambiguate which
+ * nearby device should respond. When the proximity is unknown, the proximity value will not
+ * be stored. This mapping will be excluded from the max bundle size calculation because this
+ * mapping is included after the result is returned from the hotword detector service.
+ *
* <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents
* that can be used to communicate with other processes.
*/
@@ -882,10 +931,10 @@ public final class HotwordDetectedResult implements Parcelable {
}
@DataClass.Generated(
- time = 1625541522353L,
+ time = 1658357814396L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index a6f63e859049..52dc34298ae7 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -89,9 +89,7 @@ public final class AccessibilityInteractionController {
// Callbacks should have the same configuration of the flags below to allow satisfying a pending
// node request on prefetch
- private static final int FLAGS_AFFECTING_REPORTED_DATA =
- AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ private static final int FLAGS_AFFECTING_REPORTED_DATA = AccessibilityNodeInfo.FLAG_REPORT_MASK;
private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
@@ -167,6 +165,11 @@ public final class AccessibilityInteractionController {
return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
}
+ private boolean isVisibleToAccessibilityService(View view) {
+ return view != null && (!view.isAccessibilityDataPrivate()
+ || mA11yManager.isRequestFromAccessibilityTool());
+ }
+
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
@@ -358,7 +361,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
requestedView = findViewByAccessibilityId(accessibilityViewId);
if (requestedView != null && isShown(requestedView)) {
requestedNode = populateAccessibilityNodeInfoForView(
@@ -371,7 +374,7 @@ public final class AccessibilityInteractionController {
mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
infos);
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
}
}
} finally {
@@ -396,7 +399,7 @@ public final class AccessibilityInteractionController {
}
mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos);
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos,
@@ -478,7 +481,7 @@ public final class AccessibilityInteractionController {
|| viewId == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null) {
final int resolvedViewId = root.getContext().getResources()
@@ -494,7 +497,7 @@ public final class AccessibilityInteractionController {
mAddNodeInfosForViewId.reset();
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -542,7 +545,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
@@ -561,7 +564,7 @@ public final class AccessibilityInteractionController {
final int viewCount = foundViews.size();
for (int i = 0; i < viewCount; i++) {
View foundView = foundViews.get(i);
- if (isShown(foundView)) {
+ if (isShown(foundView) && isVisibleToAccessibilityService(foundView)) {
provider = foundView.getAccessibilityNodeProvider();
if (provider != null) {
List<AccessibilityNodeInfo> infosFromProvider =
@@ -579,7 +582,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -627,7 +630,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
switch (focusType) {
@@ -642,6 +645,9 @@ public final class AccessibilityInteractionController {
if (!isShown(host)) {
break;
}
+ if (!isVisibleToAccessibilityService(host)) {
+ break;
+ }
// If the host has a provider ask this provider to search for the
// focus instead fetching all provider nodes to do the search here.
AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
@@ -662,6 +668,9 @@ public final class AccessibilityInteractionController {
if (!isShown(target)) {
break;
}
+ if (!isVisibleToAccessibilityService(target)) {
+ break;
+ }
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
focused = provider.findFocus(focusType);
@@ -675,7 +684,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfoForViewportAndReturnFindNodeResult(
focused, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -722,7 +731,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
View nextView = root.focusSearch(direction);
@@ -731,7 +740,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfoForViewportAndReturnFindNodeResult(
next, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -778,9 +787,9 @@ public final class AccessibilityInteractionController {
mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View target = findViewByAccessibilityId(accessibilityViewId);
- if (target != null && isShown(target)) {
+ if (target != null && isShown(target) && isVisibleToAccessibilityService(target)) {
mA11yManager.notifyPerformingAction(action);
if (action == R.id.accessibilityActionClickOnClickableSpan) {
// Handle this hidden action separately
@@ -799,7 +808,7 @@ public final class AccessibilityInteractionController {
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
callback.setPerformAccessibilityActionResult(succeeded, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -823,9 +832,9 @@ public final class AccessibilityInteractionController {
return;
}
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags =
- AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
- final View root = mViewRootImpl.mView;
+ setAccessibilityFetchFlags(
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS);
+ final View root = getRootView();
if (root != null && isShown(root)) {
final View host = mViewRootImpl.mAccessibilityFocusedHost;
// If there is no accessibility focus host or it is not a descendant
@@ -849,7 +858,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
}
}
@@ -869,7 +878,7 @@ public final class AccessibilityInteractionController {
|| mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
- final View root = mViewRootImpl.mView;
+ final View root = getRootView();
if (root != null && isShown(root)) {
// trigger ACTION_OUTSIDE to notify windows
final long now = SystemClock.uptimeMillis();
@@ -882,12 +891,30 @@ public final class AccessibilityInteractionController {
private View findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
- return mViewRootImpl.mView;
+ return getRootView();
} else {
return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
}
}
+ private View getRootView() {
+ if (!isVisibleToAccessibilityService(mViewRootImpl.mView)) {
+ return null;
+ }
+ return mViewRootImpl.mView;
+ }
+
+ private void setAccessibilityFetchFlags(int flags) {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ mA11yManager.setRequestFromAccessibilityTool(
+ (flags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) != 0);
+ }
+
+ private void resetAccessibilityFetchFlags() {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ mA11yManager.setRequestFromAccessibilityTool(false);
+ }
+
// The boundInScreen includes magnification effect, so we need to normalize it before
// determine the visibility.
private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
@@ -1706,7 +1733,7 @@ public final class AccessibilityInteractionController {
@Override
public boolean test(View view) {
- if (view.getId() == mViewId && isShown(view)) {
+ if (view.getId() == mViewId && isShown(view) && isVisibleToAccessibilityService(view)) {
mInfos.add(view.createAccessibilityNodeInfo());
}
return false;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 48937770eddb..84edb3a7bdee 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3085,6 +3085,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO;
/**
+ * Automatically determine whether the view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * Accessibility interactions from services without {@code isAccessibilityTool} set to true are
+ * disallowed for any of the following conditions:
+ * <li>this view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.</li>
+ * <li>this view sets {@link #getFilterTouchesWhenObscured()}.</li>
+ * <li>any parent of this view returns true from {@link #isAccessibilityDataPrivate()}.</li>
+ * </p>
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_AUTO = 0x00000000;
+
+ /**
+ * Only allow interactions from {@link android.accessibilityservice.AccessibilityService}s
+ * with the {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_YES = 0x00000001;
+
+ /**
+ * Allow interactions from all {@link android.accessibilityservice.AccessibilityService}s,
+ * regardless of their
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property.
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_NO = 0x00000002;
+
+ /** @hide */
+ @IntDef(prefix = { "ACCESSIBILITY_DATA_PRIVATE_" }, value = {
+ ACCESSIBILITY_DATA_PRIVATE_AUTO,
+ ACCESSIBILITY_DATA_PRIVATE_YES,
+ ACCESSIBILITY_DATA_PRIVATE_NO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AccessibilityDataPrivate {}
+
+ /**
* Mask for obtaining the bits which specify how to determine
* whether a view is important for accessibility.
*/
@@ -4527,6 +4566,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private CharSequence mAccessibilityPaneTitle;
/**
+ * Describes whether this view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ */
+ private int mAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_AUTO;
+
+ /**
* Specifies the id of a view for which this view serves as a label for
* accessibility purposes.
*/
@@ -5919,6 +5966,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setImportantForAccessibility(a.getInt(attr,
IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
break;
+ case R.styleable.View_accessibilityDataPrivate:
+ setAccessibilityDataPrivate(a.getInt(attr,
+ ACCESSIBILITY_DATA_PRIVATE_AUTO));
+ break;
case R.styleable.View_accessibilityLiveRegion:
setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
break;
@@ -8518,6 +8569,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* is responsible for handling this call.
* </p>
* <p>
+ * If this view sets {@link #isAccessibilityDataPrivate()} then this view should only append
+ * sensitive information to an event that also sets
+ * {@link AccessibilityEvent#isAccessibilityDataPrivate()}.
+ * </p>
+ * <p>
* <em>Note:</em> Accessibility events of certain types are not dispatched for
* populating the event text via this method. For details refer to {@link AccessibilityEvent}.
* </p>
@@ -10419,7 +10475,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if ((mAttachInfo.mAccessibilityFetchFlags
- & AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS) != 0
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS) != 0
&& Resources.resourceHasPackage(mID)) {
try {
String viewId = getResources().getResourceName(mID);
@@ -14402,14 +14458,75 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@UnsupportedAppUsage
public boolean includeForAccessibility() {
if (mAttachInfo != null) {
+ if (isAccessibilityDataPrivate() && !AccessibilityManager.getInstance(
+ mContext).isRequestFromAccessibilityTool()) {
+ return false;
+ }
+
return (mAttachInfo.mAccessibilityFetchFlags
- & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
|| isImportantForAccessibility();
}
return false;
}
/**
+ * Whether this view should restrict accessibility service access only to services that have the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * See default behavior provided by {@link #ACCESSIBILITY_DATA_PRIVATE_AUTO}. Otherwise,
+ * returns true for {@link #ACCESSIBILITY_DATA_PRIVATE_YES} or false for {@link
+ * #ACCESSIBILITY_DATA_PRIVATE_NO}.
+ * </p>
+ *
+ * @return True if this view should restrict accessibility service access to services that have
+ * the isAccessibilityTool property.
+ */
+ @ViewDebug.ExportedProperty(category = "accessibility")
+ public boolean isAccessibilityDataPrivate() {
+ if (mAccessibilityDataPrivate == ACCESSIBILITY_DATA_PRIVATE_YES) {
+ return true;
+ }
+ if (mAccessibilityDataPrivate == ACCESSIBILITY_DATA_PRIVATE_NO) {
+ return false;
+ }
+
+ // Views inside FLAG_SECURE windows default to accessibilityDataPrivate.
+ if (mAttachInfo != null && mAttachInfo.mWindowSecure) {
+ return true;
+ }
+ // Views that set filterTouchesWhenObscured default to accessibilityDataPrivate.
+ if (getFilterTouchesWhenObscured()) {
+ return true;
+ }
+
+ // Descendants of an accessibilityDataPrivate View are also accessibilityDataPrivate.
+ ViewParent parent = mParent;
+ while (parent instanceof View) {
+ if (((View) parent).isAccessibilityDataPrivate()) {
+ return true;
+ }
+ parent = parent.getParent();
+ }
+
+ // Otherwise, default to not accessibilityDataPrivate.
+ return false;
+ }
+
+ /**
+ * Specifies whether this view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ */
+ public void setAccessibilityDataPrivate(
+ @AccessibilityDataPrivate int accessibilityDataPrivate) {
+ mAccessibilityDataPrivate = accessibilityDataPrivate;
+ }
+
+ /**
* Returns whether the View is considered actionable from
* accessibility perspective. Such view are important for
* accessibility.
@@ -30096,6 +30213,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int mWindowVisibility;
/**
+ * Indicates whether the view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.
+ */
+ boolean mWindowSecure;
+
+ /**
* Indicates the time at which drawing started to occur.
*/
@UnsupportedAppUsage
@@ -30272,8 +30394,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Flags related to accessibility processing.
*
- * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS
+ * @see AccessibilityNodeInfo#FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @see AccessibilityNodeInfo#FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
*/
int mAccessibilityFetchFlags;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b2854a3fec44..f79f0d49959d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2839,6 +2839,7 @@ public final class ViewRootImpl implements ViewParent,
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mWindowVisibility = viewVisibility;
+ mAttachInfo.mWindowSecure = (lp.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6f6936127cde..fe8d64f0152f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -4470,10 +4470,22 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
- if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) {
+ if (paramsForRotation != o.paramsForRotation) {
+ if ((changes & LAYOUT_CHANGED) == 0) {
+ if (paramsForRotation != null && o.paramsForRotation != null
+ && paramsForRotation.length == o.paramsForRotation.length) {
+ for (int i = paramsForRotation.length - 1; i >= 0; i--) {
+ if (hasLayoutDiff(paramsForRotation[i], o.paramsForRotation[i])) {
+ changes |= LAYOUT_CHANGED;
+ break;
+ }
+ }
+ } else {
+ changes |= LAYOUT_CHANGED;
+ }
+ }
paramsForRotation = o.paramsForRotation;
checkNonRecursiveParams();
- changes |= LAYOUT_CHANGED;
}
if (mWallpaperTouchEventsEnabled != o.mWallpaperTouchEventsEnabled) {
@@ -4484,6 +4496,21 @@ public interface WindowManager extends ViewManager {
return changes;
}
+ /**
+ * Returns {@code true} if the 2 params may have difference results of
+ * {@link WindowLayout#computeFrames}.
+ */
+ private static boolean hasLayoutDiff(LayoutParams a, LayoutParams b) {
+ return a.width != b.width || a.height != b.height || a.x != b.x || a.y != b.y
+ || a.horizontalMargin != b.horizontalMargin
+ || a.verticalMargin != b.verticalMargin
+ || a.layoutInDisplayCutoutMode != b.layoutInDisplayCutoutMode
+ || a.gravity != b.gravity || !Arrays.equals(a.providedInsets, b.providedInsets)
+ || a.mFitInsetsTypes != b.mFitInsetsTypes
+ || a.mFitInsetsSides != b.mFitInsetsSides
+ || a.mFitInsetsIgnoringVisibility != b.mFitInsetsIgnoringVisibility;
+ }
+
@Override
public String debug(String output) {
output += "Contents of " + this + ":";
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index cd0dd1df1249..2db0dcbce45e 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -46,15 +46,17 @@ import java.util.List;
* </p>
* <p>
* The main purpose of an accessibility event is to communicate changes in the UI to an
- * {@link android.accessibilityservice.AccessibilityService}. The service may then inspect,
- * if needed the user interface by examining the View hierarchy, as represented by a tree of
- * {@link AccessibilityNodeInfo}s (snapshot of a View state)
- * which can be used for exploring the window content. Note that the privilege for accessing
- * an event's source, thus the window content, has to be explicitly requested. For more
+ * {@link android.accessibilityservice.AccessibilityService}. If needed, the service may then
+ * inspect the user interface by examining the View hierarchy through the event's
+ * {@link #getSource() source}, as represented by a tree of {@link AccessibilityNodeInfo}s (snapshot
+ * of a View state) which can be used for exploring the window content. Note that the privilege for
+ * accessing an event's source, thus the window content, has to be explicitly requested. For more
* details refer to {@link android.accessibilityservice.AccessibilityService}. If an
* accessibility service has not requested to retrieve the window content the event will
- * not contain reference to its source. Also for events of type
- * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available.
+ * not contain reference to its source. <strong>Note: </strong> for events of type
+ * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available, and Views that set
+ * {@link android.view.View#isAccessibilityDataPrivate()} may not populate all event properties on
+ * events sent from higher up in the view hierarchy.
* </p>
* <p>
* This class represents various semantically different accessibility event
@@ -1092,6 +1094,47 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
+ * Whether the event should only be delivered to an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * Initial value matches the {@link android.view.View#isAccessibilityDataPrivate} property from
+ * the event's source node, if present, or false by default.
+ * </p>
+ *
+ * @return True if the event should be delivered only to isAccessibilityTool services, false
+ * otherwise.
+ * @see #setAccessibilityDataPrivate
+ */
+ @Override
+ public boolean isAccessibilityDataPrivate() {
+ return super.isAccessibilityDataPrivate();
+ }
+
+ /**
+ * Sets whether the event should only be delivered to an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * This will be set automatically based on the event's source (if present). If creating and
+ * sending an event directly through {@link AccessibilityManager} (where an event may have
+ * no source) then this method must be called explicitly if you want non-default behavior.
+ * </p>
+ *
+ * @param accessibilityDataPrivate True if the event should be delivered only to
+ * isAccessibilityTool services, false otherwise.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ @Override
+ public void setAccessibilityDataPrivate(boolean accessibilityDataPrivate) {
+ super.setAccessibilityDataPrivate(accessibilityDataPrivate);
+ }
+
+ /**
* Gets the bit mask of the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
*
* @see #SPEECH_STATE_SPEAKING_START
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 9e3195aec8a6..e89f836aaac1 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -276,6 +276,8 @@ public final class AccessibilityManager {
private final ArrayMap<AudioDescriptionRequestedChangeListener, Executor>
mAudioDescriptionRequestedChangeListeners = new ArrayMap<>();
+ private boolean mRequestFromAccessibilityTool;
+
/**
* Map from a view's accessibility id to the list of request preparers set for that view
*/
@@ -983,6 +985,39 @@ public final class AccessibilityManager {
}
/**
+ * Whether the current accessibility request comes from an
+ * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ *
+ * <p>
+ * You can use this method inside {@link AccessibilityNodeProvider} to decide how to populate
+ * your nodes.
+ * </p>
+ *
+ * <p>
+ * <strong>Note:</strong> The return value is valid only when an {@link AccessibilityNodeInfo}
+ * request is in progress, can change from one request to another, and has no meaning when a
+ * request is not in progress.
+ * </p>
+ *
+ * @return True if the current request is from a tool that sets isAccessibilityTool.
+ */
+ public boolean isRequestFromAccessibilityTool() {
+ return mRequestFromAccessibilityTool;
+ }
+
+ /**
+ * Specifies whether the current accessibility request comes from an
+ * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ *
+ * @hide
+ */
+ public void setRequestFromAccessibilityTool(boolean requestFromAccessibilityTool) {
+ mRequestFromAccessibilityTool = requestFromAccessibilityTool;
+ }
+
+ /**
* Registers a {@link AccessibilityRequestPreparer}.
*/
public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 953f2615b539..15718c4af26f 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -217,14 +217,29 @@ public class AccessibilityNodeInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface PrefetchingStrategy {}
- /** @hide */
- public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080;
+ /**
+ * @see AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @hide
+ */
+ public static final int FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080;
- /** @hide */
- public static final int FLAG_REPORT_VIEW_IDS = 0x00000100;
+ /**
+ * @see AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS
+ * @hide
+ */
+ public static final int FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS = 0x00000100;
+
+ /**
+ * @see AccessibilityServiceInfo#isAccessibilityTool()
+ * @hide
+ */
+ public static final int FLAG_SERVICE_IS_ACCESSIBILITY_TOOL = 0x00000200;
/** @hide */
- public static final int FLAG_REPORT_MASK = 0x00000180;
+ public static final int FLAG_REPORT_MASK =
+ FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
+ | FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
+ | FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
// Actions.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 036316e15cb9..789c740bbba2 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -72,6 +72,7 @@ public class AccessibilityRecord {
private static final int PROPERTY_FULL_SCREEN = 0x00000080;
private static final int PROPERTY_SCROLLABLE = 0x00000100;
private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200;
+ private static final int PROPERTY_ACCESSIBILITY_DATA_PRIVATE = 0x00000400;
private static final int GET_SOURCE_PREFETCH_FLAGS =
AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS
@@ -159,6 +160,8 @@ public class AccessibilityRecord {
important = root.isImportantForAccessibility();
rootViewId = root.getAccessibilityViewId();
mSourceWindowId = root.getAccessibilityWindowId();
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE,
+ root.isAccessibilityDataPrivate());
}
setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
@@ -388,6 +391,23 @@ public class AccessibilityRecord {
}
/**
+ * @see AccessibilityEvent#isAccessibilityDataPrivate
+ * @hide
+ */
+ boolean isAccessibilityDataPrivate() {
+ return getBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE);
+ }
+
+ /**
+ * @see AccessibilityEvent#setAccessibilityDataPrivate
+ * @hide
+ */
+ void setAccessibilityDataPrivate(boolean accessibilityDataPrivate) {
+ enforceNotSealed();
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE, accessibilityDataPrivate);
+ }
+
+ /**
* Gets the number of items that can be visited.
*
* @return The number of items.
@@ -941,6 +961,8 @@ public class AccessibilityRecord {
appendUnless(false, PROPERTY_CHECKED, builder);
appendUnless(false, PROPERTY_FULL_SCREEN, builder);
appendUnless(false, PROPERTY_SCROLLABLE, builder);
+ appendUnless(false, PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, builder);
+ appendUnless(false, PROPERTY_ACCESSIBILITY_DATA_PRIVATE, builder);
append(builder, "BeforeText", mBeforeText);
append(builder, "FromIndex", mFromIndex);
@@ -974,6 +996,8 @@ public class AccessibilityRecord {
case PROPERTY_SCROLLABLE: return "Scrollable";
case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY:
return "ImportantForAccessibility";
+ case PROPERTY_ACCESSIBILITY_DATA_PRIVATE:
+ return "AccessibilityDataPrivate";
default: return Integer.toHexString(prop);
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0b4a29899020..0c03d109f649 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -3605,7 +3605,9 @@ public final class InputMethodManager {
* specifying one of those statically defined subtypes in {@code subtypes}.</p>
*
* @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
+ * If the imiId is {@code null}, system would do nothing for this operation.
* @param subtypes subtypes will be added as additional subtypes of the current input method.
+ * If the subtypes is {@code null}, system would do nothing for this operation.
* @deprecated For IMEs that have already implemented features like customizable/downloadable
* keyboard layouts/languages, please start migration to other approaches. One idea
* would be exposing only one unified {@link InputMethodSubtype} then implement
@@ -3618,6 +3620,11 @@ public final class InputMethodManager {
mServiceInvoker.setAdditionalInputMethodSubtypes(imiId, subtypes, UserHandle.myUserId());
}
+ /**
+ * Returns the last used {@link InputMethodSubtype} in system history.
+ *
+ * @return the last {@link InputMethodSubtype}, {@code null} if last IME have no subtype.
+ */
@Nullable
public InputMethodSubtype getLastInputMethodSubtype() {
return mServiceInvoker.getLastInputMethodSubtype(UserHandle.myUserId());
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index bd0a5162a036..56310aef7752 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -11,3 +11,5 @@ njawad@google.com
per-file TextView*,EditText.java,Editor.java,EditorTouchState.java = file:../text/OWNERS
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
+
+per-file RemoteViews* = file:../appwidget/OWNERS \ No newline at end of file
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fe0cbcb4eb7e..a0609ed6ee61 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12076,6 +12076,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
super.onPopulateAccessibilityEventInternal(event);
+ if (this.isAccessibilityDataPrivate() && !event.isAccessibilityDataPrivate()) {
+ // This view's accessibility data is private, but another view that generated this event
+ // is not, so don't append this view's text to the event in order to prevent sharing
+ // this view's contents with non-accessibility-tool services.
+ return;
+ }
+
final CharSequence text = getTextForAccessibility();
if (!TextUtils.isEmpty(text)) {
event.getText().add(text);
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 212616609f35..ff7ea317d3f8 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -269,6 +269,20 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sets whether a task should be translucent. When {@code false}, the existing translucent of
+ * the task applies, but when {@code true} the task will be forced to be translucent.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setForceTranslucent(
+ @NonNull WindowContainerToken container, boolean forceTranslucent) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mForceTranslucent = forceTranslucent;
+ chg.mChangeMask |= Change.CHANGE_FORCE_TRANSLUCENT;
+ return this;
+ }
+
+ /**
* Used in conjunction with a shell-transition call (usually finishTransition). This is
* basically a message to the transition system that a particular task should NOT go into
* PIP even though it normally would. This is to deal with some edge-case situations where
@@ -854,11 +868,13 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
+ public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
private boolean mIgnoreOrientationRequest = false;
+ private boolean mForceTranslucent = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
@@ -878,6 +894,7 @@ public final class WindowContainerTransaction implements Parcelable {
mFocusable = in.readBoolean();
mHidden = in.readBoolean();
mIgnoreOrientationRequest = in.readBoolean();
+ mForceTranslucent = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -923,6 +940,9 @@ public final class WindowContainerTransaction implements Parcelable {
if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
}
+ if ((other.mChangeMask & CHANGE_FORCE_TRANSLUCENT) != 0) {
+ mForceTranslucent = other.mForceTranslucent;
+ }
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
@@ -973,6 +993,15 @@ public final class WindowContainerTransaction implements Parcelable {
return mIgnoreOrientationRequest;
}
+ /** Gets the requested force translucent state. */
+ public boolean getForceTranslucent() {
+ if ((mChangeMask & CHANGE_FORCE_TRANSLUCENT) == 0) {
+ throw new RuntimeException("Force translucent not set. "
+ + "Check CHANGE_FORCE_TRANSLUCENT first");
+ }
+ return mForceTranslucent;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -1050,6 +1079,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeBoolean(mFocusable);
dest.writeBoolean(mHidden);
dest.writeBoolean(mIgnoreOrientationRequest);
+ dest.writeBoolean(mForceTranslucent);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto
index 856bc839aee5..2b74220a1471 100644
--- a/core/proto/android/os/batteryusagestats.proto
+++ b/core/proto/android/os/batteryusagestats.proto
@@ -102,4 +102,21 @@ message BatteryUsageStatsAtomsProto {
// Total amount of time battery was discharging during the reported session
optional int64 discharge_duration_millis = 7;
+
+ // Notes the power model used for a power component.
+ message PowerComponentModel {
+ // Holds android.os.PowerComponentEnum, or custom component value between 1000 and 9999.
+ optional int32 component = 1;
+
+ enum PowerModel {
+ UNDEFINED = 0;
+ POWER_PROFILE = 1;
+ MEASURED_ENERGY = 2;
+ }
+
+ optional PowerModel power_model = 2;
+ }
+
+ // The power model used for each power component.
+ repeated PowerComponentModel component_models = 8;
}
diff --git a/core/proto/android/os/processstarttime.proto b/core/proto/android/os/processstarttime.proto
deleted file mode 100644
index d0f8baee7da2..000000000000
--- a/core/proto/android/os/processstarttime.proto
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package android.os;
-
-option java_multiple_files = true;
-
-// This message is used for statsd logging and should be kept in sync with
-// frameworks/proto_logging/stats/atoms.proto
-/**
- * Logs information about process start time.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message ProcessStartTime {
- // The uid of the ProcessRecord.
- optional int32 uid = 1;
-
- // The process pid.
- optional int32 pid = 2;
-
- // The process name.
- // Usually package name, "system" for system server.
- // Provided by ActivityManagerService.
- optional string process_name = 3;
-
- enum StartType {
- UNKNOWN = 0;
- WARM = 1;
- HOT = 2;
- COLD = 3;
- }
-
- // The start type.
- optional StartType type = 4;
-
- // The elapsed realtime at the start of the process.
- optional int64 process_start_time_millis = 5;
-
- // Number of milliseconds it takes to reach bind application.
- optional int32 bind_application_delay_millis = 6;
-
- // Number of milliseconds it takes to finish start of the process.
- optional int32 process_start_delay_millis = 7;
-
- // hostingType field in ProcessRecord, the component type such as "activity",
- // "service", "content provider", "broadcast" or other strings.
- optional string hosting_type = 8;
-
- // hostingNameStr field in ProcessRecord. The component class name that runs
- // in this process.
- optional string hosting_name = 9;
-
- // Broadcast action name.
- optional string broadcast_action_name = 10;
-
- enum HostingTypeId {
- HOSTING_TYPE_UNKNOWN = 0;
- HOSTING_TYPE_ACTIVITY = 1;
- HOSTING_TYPE_ADDED_APPLICATION = 2;
- HOSTING_TYPE_BACKUP = 3;
- HOSTING_TYPE_BROADCAST = 4;
- HOSTING_TYPE_CONTENT_PROVIDER = 5;
- HOSTING_TYPE_LINK_FAIL = 6;
- HOSTING_TYPE_ON_HOLD = 7;
- HOSTING_TYPE_NEXT_ACTIVITY = 8;
- HOSTING_TYPE_NEXT_TOP_ACTIVITY = 9;
- HOSTING_TYPE_RESTART = 10;
- HOSTING_TYPE_SERVICE = 11;
- HOSTING_TYPE_SYSTEM = 12;
- HOSTING_TYPE_TOP_ACTIVITY = 13;
- HOSTING_TYPE_EMPTY = 14;
- }
-
- optional HostingTypeId hosting_type_id = 11;
-}
-
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
new file mode 100644
index 000000000000..58b8cc9438ae
--- /dev/null
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="350dp"
+ android:layout_gravity="center"
+ android:theme="?attr/alertDialogTheme">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fp_power_button_enrollment_title"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingLeft="20dp"/>
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <Button
+ android:id="@+id/turn_off_screen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fp_power_button_enrollment_button_text"
+ android:paddingRight="20dp"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:maxLines="1"/>
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 08471c3429e3..d43a6c59a477 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3075,6 +3075,20 @@
<enum name="noHideDescendants" value="4" />
</attr>
+ <!-- Describes whether this view should allow interactions from AccessibilityServices only
+ if the service sets the isAccessibilityTool property. -->
+ <attr name="accessibilityDataPrivate" format="integer">
+ <!-- The system determines whether the view's accessibility data is private
+ - default (recommended). -->
+ <enum name="auto" value="0" />
+ <!-- Allow interactions from AccessibilityServices only if the service sets the
+ isAccessibilityTool property. -->
+ <enum name="yes" value="1" />
+ <!-- Allow interactions from all AccessibilityServices, regardless of their
+ isAccessibilityTool property. -->
+ <enum name="no" value="2" />
+ </attr>
+
<!-- Indicates to accessibility services whether the user should be notified when
this view changes. -->
<attr name="accessibilityLiveRegion" format="integer">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d176a1d97494..215376aab11c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3492,9 +3492,9 @@
(for side fingerprint) -->
<integer name="config_sidefpsPostAuthDowntime">400</integer>
- <!-- The time (in millis) that a finger tap will wait for a power button
- before dismissing the power dialog during enrollment(for side fingerprint) -->
- <integer name="config_sidefpsEnrollPowerPressWindow">300</integer>
+ <!-- The time (in millis) the clickable toast dialog will last until
+ automatically dismissing. This is currently used in SideFpsEventHandler -->
+ <integer name="config_sideFpsToastTimeout">3000</integer>
<!-- This config is used to force VoiceInteractionService to start on certain low ram devices.
It declares the package name of VoiceInteractionService that should be started. -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 11c245b4c85c..ad7d03e43d29 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -114,6 +114,7 @@
<public name="handwritingBoundsOffsetTop" />
<public name="handwritingBoundsOffsetRight" />
<public name="handwritingBoundsOffsetBottom" />
+ <public name="accessibilityDataPrivate" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d3f2607db920..d3d049382fd0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3561,21 +3561,17 @@
<!-- [CHAR LIMIT=NONE] Message to show in upgrading dialog when the bulk of the upgrade work is done. -->
<string name="android_upgrading_complete">Finishing boot.</string>
- <!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
- is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_title">Continue setup?</string>
-
<!-- [CHAR LIMIT=NONE] Message of dialog shown to confirm device going to sleep if the power
button is pressed during fingerprint enrollment. -->
<string name="fp_power_button_enrollment_message">You pressed the power button — this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint.</string>
- <!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
- power button is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_positive_button">Turn off screen</string>
+ <!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
+ is pressed during fingerprint enrollment. -->
+ <string name="fp_power_button_enrollment_title">Tap to turn off screen</string>
- <!-- [CHAR LIMIT=20] Negative button of dialog shown to confirm device going to sleep if the
+ <!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
power button is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_negative_button">Continue setup</string>
+ <string name="fp_power_button_enrollment_button_text">Turn off screen</string>
<!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
is pressed during biometric prompt when a side fingerprint sensor is present. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e6ae7b1c8b7a..c45db7e7468f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1849,8 +1849,7 @@
<java-symbol type="string" name="fp_power_button_bp_negative_button" />
<java-symbol type="string" name="fp_power_button_enrollment_title" />
<java-symbol type="string" name="fp_power_button_enrollment_message" />
- <java-symbol type="string" name="fp_power_button_enrollment_positive_button" />
- <java-symbol type="string" name="fp_power_button_enrollment_negative_button" />
+ <java-symbol type="string" name="fp_power_button_enrollment_button_text" />
<java-symbol type="string" name="global_actions" />
<java-symbol type="string" name="global_action_power_off" />
<java-symbol type="string" name="global_action_power_options" />
@@ -2627,7 +2626,11 @@
<java-symbol type="integer" name="config_sidefpsBpPowerPressWindow"/>
<java-symbol type="integer" name="config_sidefpsKeyguardPowerPressWindow"/>
<java-symbol type="integer" name="config_sidefpsPostAuthDowntime"/>
- <java-symbol type="integer" name="config_sidefpsEnrollPowerPressWindow"/>
+ <java-symbol type="integer" name="config_sideFpsToastTimeout"/>
+
+ <!-- Clickable toast used during sidefps enrollment -->
+ <java-symbol type="layout" name="side_fps_toast" />
+ <java-symbol type="id" name="turn_off_screen" />
<!-- Face authentication messages -->
<java-symbol type="string" name="face_recalibrate_notification_name" />
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index 2ce305440ef5..f82523c5e3a1 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
+import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.UidBatteryConsumer;
@@ -69,10 +70,17 @@ public class BatteryUsageStatsPulledTest {
assertEquals(bus.getDischargeDurationMs(), proto.dischargeDurationMillis);
assertEquals(3, proto.deviceBatteryConsumer.powerComponents.length); // Only 3 are non-empty
+
+ final AggregateBatteryConsumer abc = bus.getAggregateBatteryConsumer(
+ AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
assertSameBatteryConsumer("For deviceBatteryConsumer",
bus.getAggregateBatteryConsumer(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE),
proto.deviceBatteryConsumer);
+ for (int i = 0; i < BatteryConsumer.POWER_COMPONENT_COUNT; i++) {
+ assertPowerComponentModel(i, abc.getPowerModel(i), proto);
+ }
+
// Now for the UidBatteryConsumers.
final List<android.os.UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers();
uidConsumers.sort((a, b) -> a.getUid() - b.getUid());
@@ -196,6 +204,34 @@ public class BatteryUsageStatsPulledTest {
}
}
+ /**
+ * Validates the PowerComponentModel object that matches powerComponent.
+ */
+ private void assertPowerComponentModel(int powerComponent,
+ @BatteryConsumer.PowerModel int powerModel, BatteryUsageStatsAtomsProto proto) {
+ boolean found = false;
+ for (BatteryUsageStatsAtomsProto.PowerComponentModel powerComponentModel :
+ proto.componentModels) {
+ if (powerComponentModel.component == powerComponent) {
+ if (found) {
+ fail("Power component " + BatteryConsumer.powerComponentIdToString(
+ powerComponent) + " found multiple times in the proto");
+ }
+ found = true;
+ final int expectedPowerModel = BatteryConsumer.powerModelToProtoEnum(powerModel);
+ assertEquals(expectedPowerModel, powerComponentModel.powerModel);
+ }
+ }
+ if (!found) {
+ final int model = BatteryConsumer.powerModelToProtoEnum(powerModel);
+ assertEquals(
+ "Power component " + BatteryConsumer.powerComponentIdToString(powerComponent)
+ + " was not found in the proto but has a defined power model.",
+ BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED,
+ model);
+ }
+ }
+
/** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */
private long convertMahToDc(double powerMah) {
return (long) (powerMah * 36 + 0.5);
@@ -259,11 +295,14 @@ public class BatteryUsageStatsPulledTest {
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(30000)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 20100)
+ BatteryConsumer.POWER_COMPONENT_CPU, 20100,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_AUDIO, 0) // Empty
+ BatteryConsumer.POWER_COMPONENT_AUDIO, 0,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CAMERA, 20150)
+ BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
+ BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
.setUsageDurationMillis(
@@ -275,7 +314,8 @@ public class BatteryUsageStatsPulledTest {
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ BatteryConsumer.POWER_COMPONENT_CPU, 10100,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
@@ -285,7 +325,7 @@ public class BatteryUsageStatsPulledTest {
@Test
public void testLargeAtomTruncated() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[0]);
+ new BatteryUsageStats.Builder(new String[0], true, false);
// If not truncated, this BatteryUsageStats object would generate a proto buffer
// significantly larger than 50 Kb
for (int i = 0; i < 3000; i++) {
diff --git a/core/tests/coretests/src/android/view/WindowParamsTest.java b/core/tests/coretests/src/android/view/WindowParamsTest.java
new file mode 100644
index 000000000000..49d4872922e2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/WindowParamsTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class WindowParamsTest {
+
+ @Test
+ public void testParamsForRotation() {
+ final WindowManager.LayoutParams paramsA = new WindowManager.LayoutParams();
+ initParamsForRotation(paramsA);
+ final Parcel parcel = Parcel.obtain();
+ paramsA.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ final WindowManager.LayoutParams paramsB = new WindowManager.LayoutParams(parcel);
+ assertEquals(0, paramsA.copyFrom(paramsB));
+
+ for (int i = 1; i <= 12; i++) {
+ initParamsForRotation(paramsA);
+ changeField(i, paramsA.paramsForRotation[0]);
+ assertEquals("Change not found for case " + i,
+ WindowManager.LayoutParams.LAYOUT_CHANGED, paramsA.copyFrom(paramsB));
+ }
+
+ parcel.recycle();
+ }
+
+ private static void initParamsForRotation(WindowManager.LayoutParams params) {
+ params.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int i = 0; i < 4; i++) {
+ params.paramsForRotation[i] = new WindowManager.LayoutParams();
+ }
+ }
+
+ private static void changeField(int fieldCase, WindowManager.LayoutParams params) {
+ switch (fieldCase) {
+ case 1:
+ params.width++;
+ break;
+ case 2:
+ params.height++;
+ break;
+ case 3:
+ params.x++;
+ break;
+ case 4:
+ params.y++;
+ break;
+ case 5:
+ params.horizontalMargin++;
+ break;
+ case 6:
+ params.verticalMargin++;
+ break;
+ case 7:
+ params.layoutInDisplayCutoutMode++;
+ break;
+ case 8:
+ params.gravity++;
+ break;
+ case 9:
+ params.providedInsets = new InsetsFrameProvider[0];
+ break;
+ case 10:
+ params.setFitInsetsTypes(0);
+ break;
+ case 11:
+ params.setFitInsetsSides(0);
+ break;
+ case 12:
+ params.setFitInsetsIgnoringVisibility(!params.isFitInsetsIgnoringVisibility());
+ break;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 312af4ff7bc2..ee8c41417458 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -22,7 +22,6 @@ import android.view.View
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FlingAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.FrameCallbackScheduler
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
@@ -125,12 +124,6 @@ class PhysicsAnimator<T> private constructor (target: T) {
private var defaultFling: FlingConfig = globalDefaultFling
/**
- * FrameCallbackScheduler to use if it need custom FrameCallbackScheduler, if this is null,
- * it will use the default FrameCallbackScheduler in the DynamicAnimation.
- */
- private var customScheduler: FrameCallbackScheduler? = null
-
- /**
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
* just one permanent update and end listener to the DynamicAnimations.
@@ -454,14 +447,6 @@ class PhysicsAnimator<T> private constructor (target: T) {
this.defaultFling = defaultFling
}
- /**
- * Set the custom FrameCallbackScheduler for all aniatmion in this animator. Set this with null for
- * restoring to default FrameCallbackScheduler.
- */
- fun setCustomScheduler(scheduler: FrameCallbackScheduler) {
- this.customScheduler = scheduler
- }
-
/** Starts the animations! */
fun start() {
startAction()
@@ -511,12 +496,9 @@ class PhysicsAnimator<T> private constructor (target: T) {
// springs) on this property before flinging.
cancel(animatedProperty)
- // Apply the custom animation scheduler if it not null
- val flingAnim = getFlingAnimation(animatedProperty, target)
- flingAnim.scheduler = customScheduler ?: flingAnim.scheduler
-
// Apply the configuration and start the animation.
- flingAnim.also { flingConfig.applyToAnimation(it) }.start()
+ getFlingAnimation(animatedProperty, target)
+ .also { flingConfig.applyToAnimation(it) }.start()
}
}
@@ -529,18 +511,6 @@ class PhysicsAnimator<T> private constructor (target: T) {
// Apply the configuration and start the animation.
val springAnim = getSpringAnimation(animatedProperty, target)
- // If customScheduler is exist and has not been set to the animation,
- // it should set here.
- if (customScheduler != null &&
- springAnim.scheduler != customScheduler) {
- // Cancel the animation before set animation handler
- if (springAnim.isRunning) {
- cancel(animatedProperty)
- }
- // Apply the custom scheduler handler if it not null
- springAnim.scheduler = customScheduler ?: springAnim.scheduler
- }
-
// Apply the configuration and start the animation.
springConfig.applyToAnimation(springAnim)
animationStartActions.add(springAnim::start)
@@ -596,12 +566,9 @@ class PhysicsAnimator<T> private constructor (target: T) {
}
}
- // Apply the custom animation scheduler if it not null
- val springAnim = getSpringAnimation(animatedProperty, target)
- springAnim.scheduler = customScheduler ?: springAnim.scheduler
-
// Apply the configuration and start the spring animation.
- springAnim.also { springConfig.applyToAnimation(it) }.start()
+ getSpringAnimation(animatedProperty, target)
+ .also { springConfig.applyToAnimation(it) }.start()
}
}
})
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index afc706ee9c8e..b8204d013105 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -19,6 +19,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import android.annotation.IntDef;
@@ -55,4 +56,7 @@ public class SplitScreenConstants {
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
+
+ /** Flag applied to a transition change to identify it as a divider bar for animation. */
+ public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 81e49f884503..b32c3eed2fb4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -29,7 +29,6 @@ import android.annotation.NonNull;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
-import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskSnapshot;
@@ -279,14 +278,15 @@ public class PipAnimationController {
mEndValue = endValue;
addListener(this);
addUpdateListener(this);
- mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mSurfaceControlTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mTransitionDirection = TRANSITION_DIRECTION_NONE;
}
@Override
public void onAnimationStart(Animator animation) {
mCurrentValue = mStartValue;
- onStartTransaction(mLeash, newSurfaceControlTransaction());
+ onStartTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction());
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this);
}
@@ -294,14 +294,16 @@ public class PipAnimationController {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
+ applySurfaceControlTransaction(mLeash,
+ mSurfaceControlTransactionFactory.getTransaction(),
animation.getAnimatedFraction());
}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
- final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
onEndTransaction(mLeash, tx, mTransitionDirection);
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
@@ -348,7 +350,8 @@ public class PipAnimationController {
}
void setColorContentOverlay(Context context) {
- final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
if (mContentOverlay != null) {
mContentOverlay.detach(tx);
}
@@ -357,7 +360,8 @@ public class PipAnimationController {
}
void setSnapshotContentOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
- final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
if (mContentOverlay != null) {
mContentOverlay.detach(tx);
}
@@ -406,7 +410,7 @@ public class PipAnimationController {
void setDestinationBounds(Rect destinationBounds) {
mDestinationBounds.set(destinationBounds);
if (mAnimationType == ANIM_TYPE_ALPHA) {
- onStartTransaction(mLeash, newSurfaceControlTransaction());
+ onStartTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction());
}
}
@@ -441,16 +445,6 @@ public class PipAnimationController {
mEndValue = endValue;
}
- /**
- * @return {@link SurfaceControl.Transaction} instance with vsync-id.
- */
- protected SurfaceControl.Transaction newSurfaceControlTransaction() {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- return tx;
- }
-
@VisibleForTesting
public void setSurfaceControlTransactionFactory(
PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 3ac08a66100a..b9746e338ced 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.view.Choreographer;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
@@ -234,4 +235,18 @@ public class PipSurfaceTransactionHelper {
public interface SurfaceControlTransactionFactory {
SurfaceControl.Transaction getTransaction();
}
+
+ /**
+ * Implementation of {@link SurfaceControlTransactionFactory} that returns
+ * {@link SurfaceControl.Transaction} with VsyncId being set.
+ */
+ public static class VsyncSurfaceControlTransactionFactory
+ implements SurfaceControlTransactionFactory {
+ @Override
+ public SurfaceControl.Transaction getTransaction() {
+ final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ return tx;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f747b5e00759..b46eff6c55d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -304,7 +304,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
- mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mSurfaceControlTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 5a21e0734277..44d22029a5e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -33,10 +33,6 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.Looper;
-import android.view.Choreographer;
-
-import androidx.dynamicanimation.animation.FrameCallbackScheduler;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -89,25 +85,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
- private ThreadLocal<FrameCallbackScheduler> mSfSchedulerThreadLocal =
- ThreadLocal.withInitial(() -> {
- final Looper initialLooper = Looper.myLooper();
- final FrameCallbackScheduler scheduler = new FrameCallbackScheduler() {
- @Override
- public void postFrameCallback(@androidx.annotation.NonNull Runnable runnable) {
- // TODO(b/222697646): remove getSfInstance usage and use vsyncId for
- // transactions
- Choreographer.getSfInstance().postFrameCallback(t -> runnable.run());
- }
-
- @Override
- public boolean isCurrentThread() {
- return Looper.myLooper() == initialLooper;
- }
- };
- return scheduler;
- });
-
/**
* PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
* using physics animations.
@@ -210,10 +187,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
public void init() {
- // Note: Needs to get the shell main thread sf vsync animation handler
mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
- mTemporaryBoundsPhysicsAnimator.setCustomScheduler(mSfSchedulerThreadLocal.get());
}
@NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8e1ae397ea64..4bc8e913ec4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -32,13 +32,13 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
-import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -147,9 +147,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private static final String TAG = StageCoordinator.class.getSimpleName();
- /** Flag applied to a transition change to identify it as a divider bar for animation. */
- public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
-
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
@@ -894,6 +891,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
});
mShouldUpdateRecents = false;
+ mIsDividerRemoteAnimating = false;
if (childrenToTop == null) {
mSideStage.removeAllTasks(wct, false /* toTop */);
@@ -1808,7 +1806,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
- shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
+ shouldAnimate = startPendingEnterAnimation(
+ transition, info, startTransaction, finishTransaction);
} else if (mSplitTransitions.isPendingRecent(transition)) {
shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
@@ -1836,7 +1835,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private boolean startPendingEnterAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction finishT) {
// First, verify that we actually have opened apps in both splits.
TransitionInfo.Change mainChild = null;
TransitionInfo.Change sideChild = null;
@@ -1883,8 +1883,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ " before startAnimation().");
}
- finishEnterSplitScreen(t);
- addDividerBarToTransition(info, t, true /* show */);
+ finishEnterSplitScreen(finishT);
+ addDividerBarToTransition(info, finishT, true /* show */);
return true;
}
@@ -1969,7 +1969,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return false;
}
- addDividerBarToTransition(info, t, false /* show */);
+ addDividerBarToTransition(info, finishT, false /* show */);
return true;
}
@@ -1980,23 +1980,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private void addDividerBarToTransition(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, boolean show) {
+ @NonNull SurfaceControl.Transaction finishT, boolean show) {
final SurfaceControl leash = mSplitLayout.getDividerLeash();
final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash);
- final Rect bounds = mSplitLayout.getDividerBounds();
- barChange.setStartAbsBounds(bounds);
- barChange.setEndAbsBounds(bounds);
+ mSplitLayout.getRefDividerBounds(mTempRect1);
+ barChange.setStartAbsBounds(mTempRect1);
+ barChange.setEndAbsBounds(mTempRect1);
barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK);
barChange.setFlags(FLAG_IS_DIVIDER_BAR);
// Technically this should be order-0, but this is running after layer assignment
// and it's a special case, so just add to end.
info.addChange(barChange);
- // Be default, make it visible. The remote animator can adjust alpha if it plans to animate.
+
if (show) {
- t.setAlpha(leash, 1.f);
- t.setLayer(leash, Integer.MAX_VALUE);
- t.setPosition(leash, bounds.left, bounds.top);
- t.show(leash);
+ finishT.setLayer(leash, Integer.MAX_VALUE);
+ finishT.setPosition(leash, mTempRect1.left, mTempRect1.top);
+ finishT.show(leash);
+ // Ensure divider surface are re-parented back into the hierarchy at the end of the
+ // transition. See Transition#buildFinishTransaction for more detail.
+ finishT.reparent(leash, mRootTaskLeash);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 5cce6b99fb11..e26c259b2397 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -20,9 +20,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
-import static com.android.wm.shell.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index a843b2a0ac39..45b69f17a861 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -162,13 +162,12 @@ class ScreenRotationAnimation {
.setParent(mAnimLeash)
.setBLASTLayer()
.setSecure(screenshotBuffer.containsSecureLayers())
+ .setOpaque(true)
.setCallsite("ShellRotationAnimation")
.setName("RotationLayer")
.build();
t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
- t.setPosition(mAnimLeash, 0, 0);
- t.setAlpha(mAnimLeash, 1);
t.show(mAnimLeash);
final ColorSpace colorSpace = screenshotBuffer.getColorSpace();
@@ -181,6 +180,7 @@ class ScreenRotationAnimation {
mBackColorSurface = new SurfaceControl.Builder(session)
.setParent(rootLeash)
.setColorLayer()
+ .setOpaque(true)
.setCallsite("ShellRotationAnimation")
.setName("BackColorSurface")
.build();
@@ -189,7 +189,6 @@ class ScreenRotationAnimation {
t.setLayer(mBackColorSurface, -1);
t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
- t.setAlpha(mBackColorSurface, 1);
t.show(mBackColorSurface);
}
@@ -242,7 +241,6 @@ class ScreenRotationAnimation {
t.setMatrix(mScreenshotLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- t.setAlpha(mScreenshotLayer, (float) 1.0);
}
/**
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 796b4c453f00..76209da47d55 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -339,6 +339,16 @@ public class GlobalSettingsValidators {
VALIDATORS.put(
Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT,
NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.EARLY_UPDATES_STATUS,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_NOT_STARTED),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_STARTED),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_SUCCESS),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_SKIPPED),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_ABORTED),
+ }));
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 760f147d43c6..874e57069af3 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -656,7 +656,8 @@ public class SettingsBackupTest {
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
Settings.Global.Wearable.BEDTIME_MODE,
- Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT);
+ Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT,
+ Settings.Global.Wearable.EARLY_UPDATES_STATUS);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 154a6fca9d09..26feaf979b20 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -137,6 +137,22 @@
]
}
],
+ "ironwood-postsubmit": [
+ {
+ "name": "PlatformScenarioTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.IwTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.platform.test.scenario.sysui"
+ }
+ ]
+ }
+ ],
"auto-end-to-end-postsubmit": [
{
"name": "AndroidAutomotiveHomeTests",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 203b236fcdd6..7e42e1b89b1f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -170,7 +170,7 @@ public class PipSurfaceTransactionHelper {
/** @return {@link SurfaceControl.Transaction} instance with vsync-id */
public static SurfaceControl.Transaction newSurfaceControlTransaction() {
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
return tx;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 9265f07ad284..33e8e3555eba 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -122,12 +122,13 @@ public class RemoteAnimationAdapterCompat {
IRemoteTransitionFinishedCallback finishCallback) {
final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] appsCompat =
- RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
+ RemoteAnimationTargetCompat.wrapApps(info, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapersCompat =
- RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
- // TODO(bc-unlock): Build wrapped object for non-apps target.
+ RemoteAnimationTargetCompat.wrapNonApps(
+ info, true /* wallpapers */, t, leashMap);
final RemoteAnimationTargetCompat[] nonAppsCompat =
- new RemoteAnimationTargetCompat[0];
+ RemoteAnimationTargetCompat.wrapNonApps(
+ info, false /* wallpapers */, t, leashMap);
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
boolean isReturnToHome = false;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index ef9e0951abf0..7c3b5fc52f0a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -16,7 +16,9 @@
package com.android.systemui.shared.system;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -24,6 +26,8 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -76,7 +80,7 @@ public class RemoteAnimationTargetCompat {
private final SurfaceControl mStartLeash;
- // Fields used only to unrap into RemoteAnimationTarget
+ // Fields used only to unwrap into RemoteAnimationTarget
private final Rect startBounds;
public final boolean willShowImeOnTarget;
@@ -203,8 +207,19 @@ public class RemoteAnimationTargetCompat {
public RemoteAnimationTargetCompat(TransitionInfo.Change change, int order,
TransitionInfo info, SurfaceControl.Transaction t) {
- taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
mode = newModeToLegacyMode(change.getMode());
+ taskInfo = change.getTaskInfo();
+ if (taskInfo != null) {
+ taskId = taskInfo.taskId;
+ isNotInRecents = !taskInfo.isRunning;
+ activityType = taskInfo.getActivityType();
+ windowConfiguration = taskInfo.configuration.windowConfiguration;
+ } else {
+ taskId = INVALID_TASK_ID;
+ isNotInRecents = true;
+ activityType = ACTIVITY_TYPE_UNDEFINED;
+ windowConfiguration = new WindowConfiguration();
+ }
// TODO: once we can properly sync transactions across process, then get rid of this leash.
leash = createLeash(info, change, order, t);
@@ -221,22 +236,12 @@ public class RemoteAnimationTargetCompat {
prefixOrderIndex = order;
// TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
contentInsets = new Rect(0, 0, 0, 0);
- if (change.getTaskInfo() != null) {
- isNotInRecents = !change.getTaskInfo().isRunning;
- activityType = change.getTaskInfo().getActivityType();
- } else {
- isNotInRecents = true;
- activityType = ACTIVITY_TYPE_UNDEFINED;
- }
- taskInfo = change.getTaskInfo();
allowEnterPip = change.getAllowEnterPip();
mStartLeash = null;
rotationChange = change.getEndRotation() - change.getStartRotation();
- windowType = INVALID_WINDOW_TYPE;
+ windowType = (change.getFlags() & FLAG_IS_DIVIDER_BAR) != 0
+ ? TYPE_DOCK_DIVIDER : INVALID_WINDOW_TYPE;
- windowConfiguration = change.getTaskInfo() != null
- ? change.getTaskInfo().configuration.windowConfiguration
- : new WindowConfiguration();
startBounds = change.getStartAbsBounds();
willShowImeOnTarget = (change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0;
}
@@ -251,37 +256,62 @@ public class RemoteAnimationTargetCompat {
}
/**
- * Represents a TransitionInfo object as an array of old-style targets
+ * Represents a TransitionInfo object as an array of old-style app targets
+ *
+ * @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should be
+ * populated by this function. If null, it is ignored.
+ */
+ public static RemoteAnimationTargetCompat[] wrapApps(TransitionInfo info,
+ SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+ final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
+ final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>();
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() == null) continue;
+
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ // Children always come before parent since changes are in top-to-bottom z-order.
+ if (taskInfo != null) {
+ if (childTaskTargets.contains(taskInfo.taskId)) {
+ // has children, so not a leaf. Skip.
+ continue;
+ }
+ if (taskInfo.hasParentTask()) {
+ childTaskTargets.put(taskInfo.parentTaskId, change);
+ }
+ }
+
+ final RemoteAnimationTargetCompat targetCompat =
+ new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t);
+ if (leashMap != null) {
+ leashMap.put(change.getLeash(), targetCompat.leash);
+ }
+ out.add(targetCompat);
+ }
+
+ return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
+ }
+
+ /**
+ * Represents a TransitionInfo object as an array of old-style non-app targets
*
* @param wallpapers If true, this will return wallpaper targets; otherwise it returns
* non-wallpaper targets.
* @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should be
* populated by this function. If null, it is ignored.
*/
- public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers,
+ public static RemoteAnimationTargetCompat[] wrapNonApps(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
- final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>();
+
for (int i = 0; i < info.getChanges().size(); i++) {
final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null) continue;
+
final boolean changeIsWallpaper =
(change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
if (wallpapers != changeIsWallpaper) continue;
- if (!wallpapers) {
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- // Children always come before parent since changes are in top-to-bottom z-order.
- if (taskInfo != null) {
- if (childTaskTargets.contains(taskInfo.taskId)) {
- // has children, so not a leaf. Skip.
- continue;
- }
- if (taskInfo.hasParentTask()) {
- childTaskTargets.put(taskInfo.parentTaskId, change);
- }
- }
- }
-
final RemoteAnimationTargetCompat targetCompat =
new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t);
if (leashMap != null) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 7c1ef8c76926..f6792251d282 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -128,9 +128,10 @@ public class RemoteTransitionCompat implements Parcelable {
IRemoteTransitionFinishedCallback finishedCallback) {
final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] apps =
- RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
+ RemoteAnimationTargetCompat.wrapApps(info, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapers =
- RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
+ RemoteAnimationTargetCompat.wrapNonApps(
+ info, true /* wallpapers */, t, leashMap);
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index d701f33c4c66..c790cfe7b7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs
import android.content.Intent
+import android.content.res.Configuration
import android.os.Handler
import android.os.UserManager
import android.provider.Settings
@@ -38,9 +39,11 @@ import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.dagger.QSScope
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
+import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.ViewController
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
@@ -69,18 +72,43 @@ internal class FooterActionsController @Inject constructor(
private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
private val globalSetting: GlobalSettings,
- private val handler: Handler
+ private val handler: Handler,
+ private val configurationController: ConfigurationController,
) : ViewController<FooterActionsView>(view) {
private var globalActionsDialog: GlobalActionsDialogLite? = null
private var lastExpansion = -1f
private var listening: Boolean = false
+ private var inSplitShade = false
- private val alphaAnimator = TouchAnimator.Builder()
- .addFloat(mView, "alpha", 0f, 1f)
- .setStartDelay(0.9f)
+ private val singleShadeAnimator by lazy {
+ // In single shade, the actions footer should only appear at the end of the expansion,
+ // so that it doesn't overlap with the notifications panel.
+ TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).setStartDelay(0.9f).build()
+ }
+
+ private val splitShadeAnimator by lazy {
+ // The Actions footer view has its own background which is the same color as the qs panel's
+ // background.
+ // We don't want it to fade in at the same time as the rest of the panel, otherwise it is
+ // more opaque than the rest of the panel's background. Only applies to split shade.
+ val alphaAnimator = TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).build()
+ val bgAlphaAnimator =
+ TouchAnimator.Builder()
+ .addFloat(mView, "backgroundAlpha", 0f, 1f)
+ .setStartDelay(0.9f)
+ .build()
+ // In split shade, we want the actions footer to fade in exactly at the same time as the
+ // rest of the shade, as there is no overlap.
+ TouchAnimator.Builder()
+ .addFloat(alphaAnimator, "position", 0f, 1f)
+ .addFloat(bgAlphaAnimator, "position", 0f, 1f)
.build()
+ }
+
+ private val animators: TouchAnimator
+ get() = if (inSplitShade) splitShadeAnimator else singleShadeAnimator
var visible = true
set(value) {
@@ -95,9 +123,7 @@ internal class FooterActionsController @Inject constructor(
private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
@VisibleForTesting
- internal val securityFootersSeparator = View(context).apply {
- visibility = View.GONE
- }
+ internal val securityFootersSeparator = View(context).apply { visibility = View.GONE }
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -133,6 +159,17 @@ internal class FooterActionsController @Inject constructor(
}
}
+ private val configurationListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ }
+
+ private fun updateResources() {
+ inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
+ }
+
override fun onInit() {
multiUserSwitchController.init()
securityFooterController.init()
@@ -189,6 +226,9 @@ internal class FooterActionsController @Inject constructor(
securityFooterController.setOnVisibilityChangedListener(visibilityListener)
fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener)
+ configurationController.addCallback(configurationListener)
+
+ updateResources()
updateView()
}
@@ -201,6 +241,7 @@ internal class FooterActionsController @Inject constructor(
globalActionsDialog = null
setListening(false)
multiUserSetting.isListening = false
+ configurationController.removeCallback(configurationListener)
}
fun setListening(listening: Boolean) {
@@ -224,7 +265,7 @@ internal class FooterActionsController @Inject constructor(
}
fun setExpansion(headerExpansionFraction: Float) {
- alphaAnimator.setPosition(headerExpansionFraction)
+ animators.setPosition(headerExpansionFraction)
}
fun setKeyguardShowing(showing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index 05038b7b1b1d..309ac2a66e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -27,6 +27,7 @@ import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
+import androidx.annotation.Keep
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
import com.android.systemui.R
@@ -45,6 +46,19 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private var qsDisabled = false
private var expansionAmount = 0f
+ /**
+ * Sets the alpha of the background of this view.
+ *
+ * Used from a [TouchAnimator] in the controller.
+ */
+ var backgroundAlpha: Float = 1f
+ @Keep
+ set(value) {
+ field = value
+ background?.alpha = (value * 255).toInt()
+ }
+ @Keep get
+
override fun onFinishInflate() {
super.onFinishInflate()
settingsContainer = findViewById(R.id.settings_button_container)
@@ -117,4 +131,4 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private const val TAG = "FooterActionsView"
private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE)
private val MotionEvent.string
- get() = "($id): ($x,$y)" \ No newline at end of file
+ get() = "($id): ($x,$y)"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 24168089f77a..7a98081fd1b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -557,9 +557,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void setQsExpansion(float expansion, float panelExpansionFraction,
float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
- float progress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
+ float alphaProgress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
? mFullShadeProgress : panelExpansionFraction;
- setAlphaAnimationProgress(mInSplitShade ? progress : 1);
+ setAlphaAnimationProgress(mInSplitShade ? alphaProgress : 1);
mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
@@ -600,7 +600,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
mQSPanelController.setIsOnKeyguard(onKeyguard);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
- mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
+ float footerActionsExpansion =
+ onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
+ mQSFooterActionController.setExpansion(footerActionsExpansion);
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 324c01959084..7155626a1aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -128,6 +128,8 @@ public class QSPanel extends LinearLayout implements Tunable {
if (mUsingMediaPlayer) {
mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ mHorizontalLinearLayout.setVisibility(
+ mUsingHorizontalLayout ? View.VISIBLE : View.GONE);
mHorizontalLinearLayout.setClipChildren(false);
mHorizontalLinearLayout.setClipToPadding(false);
@@ -445,6 +447,8 @@ public class QSPanel extends LinearLayout implements Tunable {
mMediaHostView = hostView;
ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
ViewGroup currentParent = (ViewGroup) hostView.getParent();
+ Log.d(getDumpableTag(), "Reattaching media host: " + horizontal
+ + ", current " + currentParent + ", new " + newParent);
if (currentParent != newParent) {
if (currentParent != null) {
currentParent.removeView(hostView);
@@ -589,6 +593,7 @@ public class QSPanel extends LinearLayout implements Tunable {
void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force) {
if (horizontal != mUsingHorizontalLayout || force) {
+ Log.d(getDumpableTag(), "setUsingHorizontalLayout: " + horizontal + ", " + force);
mUsingHorizontalLayout = horizontal;
ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
switchAllContentToParent(newParent, mTileLayout);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ec61ea6fffa8..6d5f844667e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -88,6 +88,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
public void onConfigurationChange(Configuration newConfig) {
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
+ mView.getDumpableTag());
onConfigurationChanged();
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
@@ -164,6 +166,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mHost.addCallback(mQSHostCallback);
setTiles();
mLastOrientation = getResources().getConfiguration().orientation;
+ mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
switchTileLayout(true);
mDumpManager.registerDumpable(mView.getDumpableTag(), this);
@@ -171,6 +174,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
@Override
protected void onViewDetached() {
+ mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag());
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
mHost.removeCallback(mQSHostCallback);
@@ -325,6 +329,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
/* Whether or not the panel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
if (horizontal != mUsingHorizontalLayout || force) {
+ mQSLogger.logSwitchTileLayout(horizontal, mUsingHorizontalLayout, force,
+ mView.getDumpableTag());
mUsingHorizontalLayout = horizontal;
mView.setUsingHorizontalLayout(mUsingHorizontalLayout, mMediaHost.getHostView(), force);
updateMediaDisappearParameters();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index ab795faf57e6..948fb1428780 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -155,6 +155,54 @@ class QSLogger @Inject constructor(
})
}
+ fun logOnViewAttached(orientation: Int, containerName: String) {
+ log(DEBUG, {
+ str1 = containerName
+ int1 = orientation
+ }, {
+ "onViewAttached: $str1 orientation $int1"
+ })
+ }
+
+ fun logOnViewDetached(orientation: Int, containerName: String) {
+ log(DEBUG, {
+ str1 = containerName
+ int1 = orientation
+ }, {
+ "onViewDetached: $str1 orientation $int1"
+ })
+ }
+
+ fun logOnConfigurationChanged(
+ lastOrientation: Int,
+ newOrientation: Int,
+ containerName: String
+ ) {
+ log(DEBUG, {
+ str1 = containerName
+ int1 = lastOrientation
+ int2 = newOrientation
+ }, {
+ "configuration change: $str1 orientation was $int1, now $int2"
+ })
+ }
+
+ fun logSwitchTileLayout(
+ after: Boolean,
+ before: Boolean,
+ force: Boolean,
+ containerName: String
+ ) {
+ log(DEBUG, {
+ str1 = containerName
+ bool1 = after
+ bool2 = before
+ bool3 = force
+ }, {
+ "change tile layout: $str1 horizontal=$bool1 (was $bool2), force? $bool3"
+ })
+ }
+
private fun toStateString(state: Int): String {
return when (state) {
Tile.STATE_ACTIVE -> "active"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 420f21db2c73..14cc6bf1ea41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -38,6 +38,7 @@ import android.os.Trace;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -126,6 +127,9 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
private final NotifPipelineChoreographer mChoreographer;
+ private int mConsecutiveReentrantRebuilds = 0;
+ @VisibleForTesting public static final int MAX_CONSECUTIVE_REENTRANT_REBUILDS = 3;
+
@Inject
public ShadeListBuilder(
DumpManager dumpManager,
@@ -310,7 +314,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mLogger.logOnBuildList(reason);
mAllEntries = entries;
- mChoreographer.schedule();
+ scheduleRebuild(/* reentrant = */ false);
}
};
@@ -1332,11 +1336,64 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
throw new RuntimeException("Missing default sectioner!");
}
- private void rebuildListIfBefore(@PipelineState.StateName int state) {
- mPipelineState.requireIsBefore(state);
- if (mPipelineState.is(STATE_IDLE)) {
+ private void rebuildListIfBefore(@PipelineState.StateName int rebuildState) {
+ final @PipelineState.StateName int currentState = mPipelineState.getState();
+
+ // If the pipeline is idle, requesting an invalidation is always okay, and starts a new run.
+ if (currentState == STATE_IDLE) {
+ scheduleRebuild(/* reentrant = */ false, rebuildState);
+ return;
+ }
+
+ // If the pipeline is running, it is okay to request an invalidation of a *later* stage.
+ // Since the current pipeline run hasn't run it yet, no new pipeline run is needed.
+ if (rebuildState > currentState) {
+ return;
+ }
+
+ // If the pipeline is running, it is bad to request an invalidation of *earlier* stages or
+ // the *current* stage; this will run the pipeline more often than needed, and may even
+ // cause an infinite loop of pipeline runs.
+ //
+ // Unfortunately, there are some unfixed bugs that cause reentrant pipeline runs, so we keep
+ // a counter and allow a few reentrant runs in a row between any two non-reentrant runs.
+ //
+ // It is technically possible for a *pair* of invalidations, one reentrant and one not, to
+ // trigger *each other*, alternating responsibility for pipeline runs in an infinite loop
+ // but constantly resetting the reentrant run counter. Hopefully that doesn't happen.
+ scheduleRebuild(/* reentrant = */ true, rebuildState);
+ }
+
+ private void scheduleRebuild(boolean reentrant) {
+ scheduleRebuild(reentrant, STATE_IDLE);
+ }
+
+ private void scheduleRebuild(boolean reentrant, @PipelineState.StateName int rebuildState) {
+ if (!reentrant) {
+ mConsecutiveReentrantRebuilds = 0;
mChoreographer.schedule();
+ return;
}
+
+ final @PipelineState.StateName int currentState = mPipelineState.getState();
+
+ final String rebuildStateName = PipelineState.getStateName(rebuildState);
+ final String currentStateName = PipelineState.getStateName(currentState);
+ final IllegalStateException exception = new IllegalStateException(
+ "Reentrant notification pipeline rebuild of state " + rebuildStateName
+ + " while pipeline in state " + currentStateName + ".");
+
+ mConsecutiveReentrantRebuilds++;
+
+ if (mConsecutiveReentrantRebuilds > MAX_CONSECUTIVE_REENTRANT_REBUILDS) {
+ Log.e(TAG, "Crashing after more than " + MAX_CONSECUTIVE_REENTRANT_REBUILDS
+ + " consecutive reentrant notification pipeline rebuilds.", exception);
+ throw exception;
+ }
+
+ Log.e(TAG, "Allowing " + mConsecutiveReentrantRebuilds
+ + " consecutive reentrant notification pipeline rebuild(s).", exception);
+ mChoreographer.schedule();
}
private static int countChildren(List<ListEntry> entries) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ef1e57b4cd3b..6e76691ae1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -286,7 +286,7 @@ public class PreparationCoordinator implements Coordinator {
if (isInflated(child)) {
// TODO: May want to put an animation hint here so view manager knows to treat
// this differently from a regular removal animation
- freeNotifViews(child);
+ freeNotifViews(child, "Past last visible group child");
}
}
}
@@ -379,7 +379,8 @@ public class PreparationCoordinator implements Coordinator {
mNotifInflatingFilter.invalidateList("onInflationFinished for " + logKey(entry));
}
- private void freeNotifViews(NotificationEntry entry) {
+ private void freeNotifViews(NotificationEntry entry, String reason) {
+ mLogger.logFreeNotifViews(entry, reason);
mViewBarn.removeViewForEntry(entry);
mNotifInflater.releaseViews(entry);
// TODO: clear the entry's row here, or even better, stop setting the row on the entry!
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
index 30f13152126c..c4f4ed54e2fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -31,7 +31,7 @@ class PreparationCoordinatorLogger @Inject constructor(
buffer.log(TAG, LogLevel.DEBUG, {
str1 = entry.logKey
}, {
- "NOTIF INFLATED $str1"
+ "Inflation completed for notif $str1"
})
}
@@ -40,7 +40,16 @@ class PreparationCoordinatorLogger @Inject constructor(
str1 = entry.logKey
str2 = reason
}, {
- "NOTIF INFLATION ABORTED $str1 reason=$str2"
+ "Infation aborted for notif $str1 reason=$str2"
+ })
+ }
+
+ fun logFreeNotifViews(entry: NotificationEntry, reason: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "Freeing content views for notif $str1 reason=$str2"
})
}
@@ -70,4 +79,4 @@ class PreparationCoordinatorLogger @Inject constructor(
}
}
-private const val TAG = "PreparationCoordinator" \ No newline at end of file
+private const val TAG = "PreparationCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
index 96b9aca9c64c..276375004f76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
@@ -17,11 +17,14 @@
package com.android.systemui.statusbar.phone
import android.annotation.ColorInt
+import android.app.WallpaperManager
import android.graphics.Color
+import android.os.Handler
import android.os.RemoteException
import android.view.IWindowManager
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
@@ -37,6 +40,8 @@ constructor(
private val windowManager: IWindowManager,
@Background private val backgroundExecutor: Executor,
private val dumpManager: DumpManager,
+ private val wallpaperManager: WallpaperManager,
+ @Main private val mainHandler: Handler,
) : CentralSurfacesComponent.Startable, Dumpable {
@ColorInt
@@ -46,9 +51,18 @@ constructor(
var isLetterboxBackgroundMultiColored: Boolean = false
private set
+ private val wallpaperColorsListener =
+ WallpaperManager.OnColorsChangedListener { _, _ ->
+ fetchBackgroundColorInfo()
+ }
+
override fun start() {
dumpManager.registerDumpable(javaClass.simpleName, this)
+ fetchBackgroundColorInfo()
+ wallpaperManager.addOnColorsChangedListener(wallpaperColorsListener, mainHandler)
+ }
+ private fun fetchBackgroundColorInfo() {
// Using a background executor, as binder calls to IWindowManager are blocking
backgroundExecutor.execute {
try {
@@ -62,6 +76,7 @@ constructor(
override fun stop() {
dumpManager.unregisterDumpable(javaClass.simpleName)
+ wallpaperManager.removeOnColorsChangedListener(wallpaperColorsListener)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 642e29b364d3..2ba8782c6d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -22,13 +22,17 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.leaks.LeakCheckedTest
+import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
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
@@ -42,47 +46,38 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
class FooterActionsControllerTest : LeakCheckedTest() {
- @Mock
- private lateinit var userManager: UserManager
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock
- private lateinit var userInfoController: UserInfoController
- @Mock
- private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
- @Mock
- private lateinit var multiUserSwitchController: MultiUserSwitchController
- @Mock
- private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
- @Mock
- private lateinit var globalActionsDialog: GlobalActionsDialogLite
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var securityFooterController: QSSecurityFooter
- @Mock
- private lateinit var fgsManagerController: QSFgsManagerFooter
+
+ @get:Rule var expect: Expect = Expect.create()
+
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var userInfoController: UserInfoController
+ @Mock private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
+ @Mock private lateinit var multiUserSwitchController: MultiUserSwitchController
+ @Mock private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
+ @Mock private lateinit var globalActionsDialog: GlobalActionsDialogLite
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var securityFooterController: QSSecurityFooter
+ @Mock private lateinit var fgsManagerController: QSFgsManagerFooter
@Captor
private lateinit var visibilityChangedCaptor:
ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener>
private lateinit var controller: FooterActionsController
+ private val configurationController = FakeConfigurationController()
private val metricsLogger: MetricsLogger = FakeMetricsLogger()
- private lateinit var view: FooterActionsView
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
+ private lateinit var view: FooterActionsView
private lateinit var testableLooper: TestableLooper
private lateinit var fakeSettings: FakeSettings
private lateinit var securityFooter: View
@@ -90,12 +85,15 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Before
fun setUp() {
+ // We want to make sure testable resources are always used
+ context.ensureTestableResources()
+
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
fakeSettings = FakeSettings()
whenever(multiUserSwitchControllerFactory.create(any()))
- .thenReturn(multiUserSwitchController)
+ .thenReturn(multiUserSwitchController)
whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog)
securityFooter = View(mContext)
@@ -135,7 +133,7 @@ class FooterActionsControllerTest : LeakCheckedTest() {
view.findViewById<View>(R.id.pm_lite).performClick()
// Verify clicks are logged
verify(uiEventLogger, Mockito.times(1))
- .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
+ .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
}
@Test
@@ -299,6 +297,86 @@ class FooterActionsControllerTest : LeakCheckedTest() {
assertThat(booleanCaptor.allValues.last()).isTrue()
}
+ @Test
+ fun setExpansion_inSplitShade_alphaFollowsExpansion() {
+ enableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.25f)
+ expect.that(view.alpha).isEqualTo(0.25f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.alpha).isEqualTo(0.5f)
+
+ controller.setExpansion(0.75f)
+ expect.that(view.alpha).isEqualTo(0.75f)
+
+ controller.setExpansion(1f)
+ expect.that(view.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun setExpansion_inSplitShade_backgroundAlphaFollowsExpansion_with_0_9_delay() {
+ enableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
+
+ controller.setExpansion(0.9f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
+
+ controller.setExpansion(0.91f)
+ expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.1f)
+
+ controller.setExpansion(0.95f)
+ expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.5f)
+
+ controller.setExpansion(1f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+ }
+
+ @Test
+ fun setExpansion_inSingleShade_alphaFollowsExpansion_with_0_9_delay() {
+ disableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.9f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.91f)
+ expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.1f)
+
+ controller.setExpansion(0.95f)
+ expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.5f)
+
+ controller.setExpansion(1f)
+ expect.that(view.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun setExpansion_inSingleShade_backgroundAlphaAlways1() {
+ disableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+
+ controller.setExpansion(1f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+ }
+
private fun setVisibilities(
securityFooterVisible: Boolean,
fgsFooterVisible: Boolean,
@@ -311,15 +389,52 @@ class FooterActionsControllerTest : LeakCheckedTest() {
}
private fun inflateView(): FooterActionsView {
- return LayoutInflater.from(context)
- .inflate(R.layout.footer_actions, null) as FooterActionsView
+ return LayoutInflater.from(context).inflate(R.layout.footer_actions, null)
+ as FooterActionsView
}
private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController {
- return FooterActionsController(view, multiUserSwitchControllerFactory,
- activityStarter, userManager, userTracker, userInfoController,
- deviceProvisionedController, securityFooterController, fgsManagerController,
- falsingManager, metricsLogger, globalActionsDialogProvider, uiEventLogger,
- showPMLiteButton = true, fakeSettings, Handler(testableLooper.looper))
+ return FooterActionsController(
+ view,
+ multiUserSwitchControllerFactory,
+ activityStarter,
+ userManager,
+ userTracker,
+ userInfoController,
+ deviceProvisionedController,
+ securityFooterController,
+ fgsManagerController,
+ falsingManager,
+ metricsLogger,
+ globalActionsDialogProvider,
+ uiEventLogger,
+ showPMLiteButton = true,
+ fakeSettings,
+ Handler(testableLooper.looper),
+ configurationController)
+ }
+
+ private fun enableSplitShade() {
+ setSplitShadeEnabled(true)
+ }
+
+ private fun disableSplitShade() {
+ setSplitShadeEnabled(false)
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_split_notification_shade, enabled)
+ configurationController.notifyConfigurationChanged()
+ }
+}
+
+private const val FLOAT_TOLERANCE = 0.01f
+
+private val View.backgroundAlphaFraction: Float?
+ get() {
+ return if (background != null) {
+ background.alpha / 255f
+ } else {
+ null
+ }
}
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 32c66d25d4aa..10f6ce8c0ec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -205,6 +205,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
@Test
+ public void setQsExpansion_inSplitShade_setsFooterActionsExpansion_basedOnPanelExpFraction() {
+ // Random test values without any meaning. They just have to be different from each other.
+ float expansion = 0.123f;
+ float panelExpansionFraction = 0.321f;
+ float proposedTranslation = 456f;
+ float squishinessFraction = 0.987f;
+
+ QSFragment fragment = resumeAndGetFragment();
+ enableSplitShade();
+
+ fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ squishinessFraction);
+
+ verify(mQSFooterActionController).setExpansion(panelExpansionFraction);
+ }
+
+ @Test
+ public void setQsExpansion_notInSplitShade_setsFooterActionsExpansion_basedOnExpansion() {
+ // Random test values without any meaning. They just have to be different from each other.
+ float expansion = 0.123f;
+ float panelExpansionFraction = 0.321f;
+ float proposedTranslation = 456f;
+ float squishinessFraction = 0.987f;
+
+ QSFragment fragment = resumeAndGetFragment();
+ disableSplitShade();
+
+ fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ squishinessFraction);
+
+ verify(mQSFooterActionController).setExpansion(expansion);
+ }
+
+ @Test
public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
index 360eef9d214f..cf5fa87272c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
@@ -19,12 +19,14 @@ package com.android.systemui.shared.system;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_STANDARD;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CHANGING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -64,12 +66,15 @@ public class RemoteTransitionTest extends SysuiTestCase {
@Test
public void testLegacyTargetExtract() {
TransitionInfo combined = new TransitionInfoBuilder(TRANSIT_CLOSE)
- .addChange(TRANSIT_CHANGE, FLAG_SHOW_WALLPAPER)
- .addChange(TRANSIT_CLOSE, 0 /* flags */)
- .addChange(TRANSIT_OPEN, FLAG_IS_WALLPAPER).build();
- // Check non-wallpaper extraction
- RemoteAnimationTargetCompat[] wrapped = RemoteAnimationTargetCompat.wrap(combined,
- false /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
+ .addChange(TRANSIT_CHANGE, FLAG_SHOW_WALLPAPER,
+ createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_STANDARD))
+ .addChange(TRANSIT_CLOSE, 0 /* flags */,
+ createTaskInfo(2 /* taskId */, ACTIVITY_TYPE_STANDARD))
+ .addChange(TRANSIT_OPEN, FLAG_IS_WALLPAPER, null /* taskInfo */)
+ .addChange(TRANSIT_CHANGE, FLAG_FIRST_CUSTOM, null /* taskInfo */).build();
+ // Check apps extraction
+ RemoteAnimationTargetCompat[] wrapped = RemoteAnimationTargetCompat.wrapApps(combined,
+ mock(SurfaceControl.Transaction.class), null /* leashes */);
assertEquals(2, wrapped.length);
int changeLayer = -1;
int closeLayer = -1;
@@ -86,17 +91,25 @@ public class RemoteTransitionTest extends SysuiTestCase {
assertTrue(closeLayer < changeLayer);
// Check wallpaper extraction
- RemoteAnimationTargetCompat[] wallps = RemoteAnimationTargetCompat.wrap(combined,
+ RemoteAnimationTargetCompat[] wallps = RemoteAnimationTargetCompat.wrapNonApps(combined,
true /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
assertEquals(1, wallps.length);
assertTrue(wallps[0].prefixOrderIndex < closeLayer);
assertEquals(MODE_OPENING, wallps[0].mode);
+
+ // Check non-apps extraction
+ RemoteAnimationTargetCompat[] nonApps = RemoteAnimationTargetCompat.wrapNonApps(combined,
+ false /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
+ assertEquals(1, nonApps.length);
+ assertTrue(nonApps[0].prefixOrderIndex < closeLayer);
+ assertEquals(MODE_CHANGING, nonApps[0].mode);
}
@Test
public void testLegacyTargetWrapper() {
TransitionInfo tinfo = new TransitionInfoBuilder(TRANSIT_CLOSE)
- .addChange(TRANSIT_CHANGE, FLAG_TRANSLUCENT).build();
+ .addChange(TRANSIT_CHANGE, FLAG_TRANSLUCENT,
+ createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_STANDARD)).build();
final TransitionInfo.Change change = tinfo.getChanges().get(0);
final Rect endBounds = new Rect(40, 60, 140, 200);
change.setTaskInfo(createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_HOME));
@@ -119,11 +132,12 @@ public class RemoteTransitionTest extends SysuiTestCase {
}
TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
- @TransitionInfo.ChangeFlags int flags) {
+ @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo) {
final TransitionInfo.Change change =
new TransitionInfo.Change(null /* token */, createMockSurface(true));
change.setMode(mode);
change.setFlags(flags);
+ change.setTaskInfo(taskInfo);
mInfo.addChange(change);
return this;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index dfa38abc1ff8..9f214097ea55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.util.time.FakeSystemClock;
@@ -1715,66 +1716,201 @@ public class ShadeListBuilderTest extends SysuiTestCase {
assertEquals(GroupEntry.ROOT_ENTRY, group.getPreviousParent());
}
+ static class CountingInvalidator {
+ CountingInvalidator(Pluggable pluggableToInvalidate) {
+ mPluggableToInvalidate = pluggableToInvalidate;
+ mInvalidationCount = 0;
+ }
+
+ public void setInvalidationCount(int invalidationCount) {
+ mInvalidationCount = invalidationCount;
+ }
+
+ public void maybeInvalidate() {
+ if (mInvalidationCount > 0) {
+ mPluggableToInvalidate.invalidateList("test invalidation");
+ mInvalidationCount--;
+ }
+ }
+
+ private Pluggable mPluggableToInvalidate;
+ private int mInvalidationCount;
+
+ private static final String TAG = "ShadeListBuilderTestCountingInvalidator";
+ }
+
+ @Test
+ public void testOutOfOrderPreGroupFilterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeTransformGroupsListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addPreGroupFilter(filter);
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // WHEN we try to run the pipeline and the filter is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
+ }
+
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderPreGroupFilterInvalidationThrows() {
- // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage
- NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(null);
+ public void testOutOfOrderPreGroupFilterInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeTransformGroupsListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.addPreGroupFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
- // WHEN we try to run the pipeline and the filter is invalidated
+ // WHEN we try to run the pipeline and the filter is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception IS thrown.
+ }
+
+ @Test
+ public void testNonConsecutiveOutOfOrderInvalidationDontThrowAfterTooManyRuns() {
+ // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeTransformGroupsListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addPreGroupFilter(filter);
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // WHEN we try to run the pipeline and the filter is invalidated at least
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
+ }
+
+ @Test
+ public void testOutOfOrderPrompterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a NotifPromoter that gets invalidated during the sorting stage,
+ NotifPromoter promoter = new IdPromoter(47);
+ CountingInvalidator invalidator = new CountingInvalidator(promoter);
+ OnBeforeSortListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addPromoter(promoter);
+ mListBuilder.addOnBeforeSortListener(listener);
+
+ // WHEN we try to run the pipeline and the promoter is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
addNotif(0, PACKAGE_1);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception is NOT thrown.
}
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderPrompterInvalidationThrows() {
- // GIVEN a NotifPromoter that gets invalidated during the sorting stage
+ public void testOutOfOrderPrompterInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a NotifPromoter that gets invalidated during the sorting stage,
NotifPromoter promoter = new IdPromoter(47);
- OnBeforeSortListener listener =
- (list) -> promoter.invalidateList(null);
+ CountingInvalidator invalidator = new CountingInvalidator(promoter);
+ OnBeforeSortListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.addPromoter(promoter);
mListBuilder.addOnBeforeSortListener(listener);
- // WHEN we try to run the pipeline and the promoter is invalidated
+ // WHEN we try to run the pipeline and the promoter is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
addNotif(0, PACKAGE_1);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception IS thrown.
+ }
+
+ @Test
+ public void testOutOfOrderComparatorInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a NotifComparator that gets invalidated during the finalizing stage,
+ NotifComparator comparator = new HypeComparator(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(comparator);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.setComparators(singletonList(comparator));
+ mListBuilder.addOnBeforeRenderListListener(listener);
+
+ // WHEN we try to run the pipeline and the comparator is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
}
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderComparatorInvalidationThrows() {
- // GIVEN a NotifComparator that gets invalidated during the finalizing stage
- NotifComparator comparator = new HypeComparator(PACKAGE_5);
- OnBeforeRenderListListener listener =
- (list) -> comparator.invalidateList(null);
+ public void testOutOfOrderComparatorInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a NotifComparator that gets invalidated during the finalizing stage,
+ NotifComparator comparator = new HypeComparator(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(comparator);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.setComparators(singletonList(comparator));
mListBuilder.addOnBeforeRenderListListener(listener);
- // WHEN we try to run the pipeline and the comparator is invalidated
- addNotif(0, PACKAGE_1);
+ // WHEN we try to run the pipeline and the comparator is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception IS thrown.
+ }
+
+ @Test
+ public void testOutOfOrderPreRenderFilterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addFinalizeFilter(filter);
+ mListBuilder.addOnBeforeRenderListListener(listener);
+
+ // WHEN we try to run the pipeline and the PreRenderFilter is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
}
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderPreRenderFilterInvalidationThrows() {
- // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage
- NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeRenderListListener listener = (list) -> filter.invalidateList(null);
+ public void testOutOfOrderPreRenderFilterInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener);
- // WHEN we try to run the pipeline and the PreRenderFilter is invalidated
- addNotif(0, PACKAGE_1);
+ // WHEN we try to run the pipeline and the PreRenderFilter is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception IS thrown.
}
@Test
@@ -2096,6 +2232,18 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mPipelineChoreographer.runIfScheduled();
}
+ private void runWhileScheduledUpTo(int maxRuns) {
+ int runs = 0;
+ while (mPipelineChoreographer.isScheduled()) {
+ if (runs > maxRuns) {
+ throw new IndexOutOfBoundsException(
+ "Pipeline scheduled itself more than " + maxRuns + "times");
+ }
+ runs++;
+ mPipelineChoreographer.runIfScheduled();
+ }
+ }
+
private void verifyBuiltList(ExpectedEntry ...expectedEntries) {
try {
assertEquals(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
index 44325dddd111..a2828d33375b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
@@ -16,19 +16,28 @@
package com.android.systemui.statusbar.phone
+import android.app.WallpaperManager
+import android.app.WallpaperManager.OnColorsChangedListener
import android.graphics.Color
+import android.os.Handler
+import android.os.Looper
import android.testing.AndroidTestingRunner
import android.view.IWindowManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -38,17 +47,41 @@ class LetterboxBackgroundProviderTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
private val fakeExecutor = FakeExecutor(fakeSystemClock)
+ private val mainHandler = Handler(Looper.getMainLooper())
+
+ @get:Rule var expect: Expect = Expect.create()
@Mock private lateinit var windowManager: IWindowManager
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var wallpaperManager: WallpaperManager
private lateinit var provider: LetterboxBackgroundProvider
+ private var wallpaperColorsListener: OnColorsChangedListener? = null
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- provider = LetterboxBackgroundProvider(windowManager, fakeExecutor, dumpManager)
+ setUpWallpaperManager()
+ provider =
+ LetterboxBackgroundProvider(
+ windowManager, fakeExecutor, dumpManager, wallpaperManager, mainHandler)
+ }
+
+ private fun setUpWallpaperManager() {
+ doAnswer { invocation ->
+ wallpaperColorsListener = invocation.arguments[0] as OnColorsChangedListener
+ return@doAnswer Unit
+ }
+ .`when`(wallpaperManager)
+ .addOnColorsChangedListener(any(), eq(mainHandler))
+ doAnswer {
+ wallpaperColorsListener = null
+ return@doAnswer Unit
+ }
+ .`when`(wallpaperManager)
+ .removeOnColorsChangedListener(any(OnColorsChangedListener::class.java))
}
@Test
@@ -76,6 +109,31 @@ class LetterboxBackgroundProviderTest : SysuiTestCase() {
}
@Test
+ fun letterboxBackgroundColor_returnsValueFromWindowManagerOnlyOnce() {
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.RED)
+ provider.start()
+ fakeExecutor.runAllReady()
+ expect.that(provider.letterboxBackgroundColor).isEqualTo(Color.RED)
+
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.GREEN)
+ fakeExecutor.runAllReady()
+ expect.that(provider.letterboxBackgroundColor).isEqualTo(Color.RED)
+ }
+
+ @Test
+ fun letterboxBackgroundColor_afterWallpaperChanges_returnsUpdatedColor() {
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.RED)
+ provider.start()
+ fakeExecutor.runAllReady()
+
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.GREEN)
+ wallpaperColorsListener!!.onColorsChanged(null, 0)
+ fakeExecutor.runAllReady()
+
+ assertThat(provider.letterboxBackgroundColor).isEqualTo(Color.GREEN)
+ }
+
+ @Test
fun isLetterboxBackgroundMultiColored_defaultValue_returnsFalse() {
assertThat(provider.isLetterboxBackgroundMultiColored).isEqualTo(false)
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index b34482f0964f..3324c526ecc2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -354,16 +354,24 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (supportsFlagForNotImportantViews(info)) {
if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ mFetchFlags |=
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
} else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ mFetchFlags &=
+ ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
}
}
if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
} else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
+ }
+
+ if (mAccessibilityServiceInfo.isAccessibilityTool()) {
+ mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
+ } else {
+ mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
}
mRequestTouchExplorationMode = (info.flags
@@ -1522,9 +1530,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
+ final boolean includeNotImportantViews = (mFetchFlags
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
&& !event.isImportantForAccessibility()
- && (mFetchFlags & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
+ && !includeNotImportantViews) {
+ return false;
+ }
+
+ if (event.isAccessibilityDataPrivate()
+ && (mFetchFlags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) == 0) {
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6eabc981e9fe..6a6d2bb44d48 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3693,6 +3693,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ info.setAccessibilityTool(true);
final AccessibilityUserState userState;
synchronized (mLock) {
userState = getCurrentUserStateLocked();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 38275f7cd348..1c571a7036ad 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -25,7 +25,6 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
-import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -284,6 +283,7 @@ public class BackupManagerService extends IBackupManager.Stub {
*/
@Override
public boolean isUserReadyForBackup(int userId) {
+ enforceCallingPermissionOnUserId(userId, "isUserReadyForBackup()");
return mUserServices.get(UserHandle.USER_SYSTEM) != null
&& mUserServices.get(userId) != null;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7f1cf0339460..0d5944e46b3c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5014,7 +5014,8 @@ public class ActivityManagerService extends IActivityManager.Stub
hostingRecord.getType(),
hostingRecord.getName(),
shortAction,
- HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()));
+ HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()),
+ HostingRecord.getTriggerTypeForStatsd(hostingRecord.getTriggerType()));
return true;
}
@@ -14339,10 +14340,12 @@ public class ActivityManagerService extends IActivityManager.Stub
if (oldRecord.resultTo != null) {
final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
try {
+ oldRecord.mIsReceiverAppRunning = true;
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
- false, false, oldRecord.userId, oldRecord.callingUid, callingUid);
+ false, false, oldRecord.userId, oldRecord.callingUid, callingUid,
+ SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ queue.mQueueName + "] sending broadcast result of "
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 768fdfd4ed5c..cc2b693d36f7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -758,6 +758,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.setMaxStatsAgeMs(0)
.includeProcessStateData()
.includeVirtualUids()
+ .includePowerModels()
.build();
bus = getBatteryUsageStats(List.of(querySinceReset)).get(0);
break;
@@ -768,6 +769,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.includeProcessStateData()
.includeVirtualUids()
.powerProfileModeledOnly()
+ .includePowerModels()
.build();
bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0);
break;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6d520c3c6be8..cd5ea14bdc92 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -321,7 +321,7 @@ public final class BroadcastQueue {
}
private final void processCurBroadcastLocked(BroadcastRecord r,
- ProcessRecord app, int receiverType, int processTemperature) throws RemoteException {
+ ProcessRecord app) throws RemoteException {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " for app " + app);
final IApplicationThread thread = app.getThread();
@@ -367,10 +367,6 @@ public final class BroadcastQueue {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, app.uid,
- r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
- ActivityManagerService.getShortAction(r.intent.getAction()),
- receiverType, processTemperature);
} finally {
if (!started) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
@@ -407,9 +403,8 @@ public final class BroadcastQueue {
}
try {
mPendingBroadcast = null;
- processCurBroadcastLocked(br, app,
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD);
+ br.mIsReceiverAppRunning = false;
+ processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting receiver "
@@ -517,6 +512,22 @@ public final class BroadcastQueue {
final long finishTime = SystemClock.uptimeMillis();
final long elapsed = finishTime - r.receiverTime;
r.state = BroadcastRecord.IDLE;
+ final int curIndex = r.nextReceiver - 1;
+ if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
+ final Object curReceiver = r.receivers.get(curIndex);
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
+ r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
+ ActivityManagerService.getShortAction(r.intent.getAction()),
+ curReceiver instanceof BroadcastFilter
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
+ : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
+ r.mIsReceiverAppRunning
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
+ : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime,
+ finishTime - r.receiverTime);
+ }
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
@@ -640,7 +651,8 @@ public final class BroadcastQueue {
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser,
- int receiverUid, int callingUid) throws RemoteException {
+ int receiverUid, int callingUid, long dispatchDelay,
+ long receiveDelay) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
final IApplicationThread thread = app.getThread();
@@ -674,12 +686,15 @@ public final class BroadcastQueue {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
- receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
- callingUid == -1 ? Process.SYSTEM_UID : callingUid,
- ActivityManagerService.getShortAction(intent.getAction()),
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
+ if (!ordered) {
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
+ receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
+ callingUid == -1 ? Process.SYSTEM_UID : callingUid,
+ ActivityManagerService.getShortAction(intent.getAction()),
+ BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
+ BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
+ dispatchDelay, receiveDelay, 0 /* finish_delay */);
+ }
}
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
@@ -983,7 +998,9 @@ public final class BroadcastQueue {
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId,
- filter.receiverList.uid, r.callingUid);
+ filter.receiverList.uid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime);
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
if (filter.receiverList.app != null
@@ -1166,6 +1183,7 @@ public final class BroadcastQueue {
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
+ r.mIsReceiverAppRunning = true;
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
@@ -1333,10 +1351,18 @@ public final class BroadcastQueue {
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ r.intent.getAction() + " app=" + r.callerApp);
}
+ if (r.dispatchTime == 0) {
+ // The dispatch time here could be 0, in case it's a parallel
+ // broadcast but it has a result receiver. Set it to now.
+ r.dispatchTime = now;
+ }
+ r.mIsReceiverAppRunning = true;
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId,
- r.callingUid, r.callingUid);
+ r.callingUid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ now - r.dispatchTime);
logBootCompletedBroadcastCompletionLatencyIfPossible(r);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
@@ -1493,6 +1519,7 @@ public final class BroadcastQueue {
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
+ r.mIsReceiverAppRunning = true;
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
@@ -1856,9 +1883,8 @@ public final class BroadcastQueue {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddAllowBackgroundActivityStartsToken(app, r);
- processCurBroadcastLocked(r, app,
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
+ r.mIsReceiverAppRunning = true;
+ processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when sending broadcast to "
@@ -1892,7 +1918,8 @@ public final class BroadcastQueue {
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
- r.intent.getAction()),
+ r.intent.getAction(), (r.alarm ? HostingRecord.TRIGGER_TYPE_ALARM
+ : HostingRecord.TRIGGER_TYPE_UNKNOWN)),
isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
if (r.curApp == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index ae91d75ef0ce..ce4528bca887 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -135,6 +135,8 @@ final class BroadcastRecord extends Binder {
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // info about the receiver that is currently running.
+ boolean mIsReceiverAppRunning; // Was the receiver's app already running.
+
// Private refcount-management bookkeeping; start > 0
static AtomicInteger sNextToken = new AtomicInteger(1);
diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java
index f88a8ce83d02..efc2a2719dad 100644
--- a/services/core/java/com/android/server/am/HostingRecord.java
+++ b/services/core/java/com/android/server/am/HostingRecord.java
@@ -16,10 +16,27 @@
package com.android.server.am;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ADDED_APPLICATION;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BACKUP;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BROADCAST;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_CONTENT_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_EMPTY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_LINK_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_TOP_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ON_HOLD;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_RESTART;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TYPE__UNKNOWN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
-import android.os.ProcessStartTime;
/**
* This class describes various information required to start a process.
@@ -32,6 +49,9 @@ import android.os.ProcessStartTime;
*
* The {@code mHostingZygote} field describes from which Zygote the new process should be spawned.
*
+ * The {@code mTriggerType} field describes the trigger that started this processs. This could be
+ * an alarm or a push-message for a broadcast, for example. This is purely for logging and stats.
+ *
* {@code mDefiningPackageName} contains the packageName of the package that defines the
* component we want to start; this can be different from the packageName and uid in the
* ApplicationInfo that we're creating the process with, in case the service is a
@@ -71,7 +91,10 @@ public final class HostingRecord {
public static final String HOSTING_TYPE_TOP_ACTIVITY = "top-activity";
public static final String HOSTING_TYPE_EMPTY = "";
- private @NonNull final String mHostingType;
+ public static final String TRIGGER_TYPE_UNKNOWN = "unknown";
+ public static final String TRIGGER_TYPE_ALARM = "alarm";
+
+ @NonNull private final String mHostingType;
private final String mHostingName;
private final int mHostingZygote;
private final String mDefiningPackageName;
@@ -79,11 +102,12 @@ public final class HostingRecord {
private final boolean mIsTopApp;
private final String mDefiningProcessName;
@Nullable private final String mAction;
+ @NonNull private final String mTriggerType;
public HostingRecord(@NonNull String hostingType) {
this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */,
-1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */,
- null /* action */);
+ null /* action */, TRIGGER_TYPE_UNKNOWN);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName) {
@@ -91,22 +115,23 @@ public final class HostingRecord {
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName,
- @Nullable String action) {
+ @Nullable String action, @Nullable String triggerType) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
null /* definingPackageName */, -1 /* mDefiningUid */, false /* isTopApp */,
- null /* definingProcessName */, action);
+ null /* definingProcessName */, action, triggerType);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName,
String definingPackageName, int definingUid, String definingProcessName) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName,
- definingUid, false /* isTopApp */, definingProcessName, null /* action */);
+ definingUid, false /* isTopApp */, definingProcessName, null /* action */,
+ TRIGGER_TYPE_UNKNOWN);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName, boolean isTopApp) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */,
- null /* definingProcessName */, null /* action */);
+ null /* definingProcessName */, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
public HostingRecord(@NonNull String hostingType, String hostingName) {
@@ -121,12 +146,12 @@ public final class HostingRecord {
private HostingRecord(@NonNull String hostingType, String hostingName, int hostingZygote) {
this(hostingType, hostingName, hostingZygote, null /* definingPackageName */,
-1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */,
- null /* action */);
+ null /* action */, TRIGGER_TYPE_UNKNOWN);
}
private HostingRecord(@NonNull String hostingType, String hostingName, int hostingZygote,
String definingPackageName, int definingUid, boolean isTopApp,
- String definingProcessName, @Nullable String action) {
+ String definingProcessName, @Nullable String action, String triggerType) {
mHostingType = hostingType;
mHostingName = hostingName;
mHostingZygote = hostingZygote;
@@ -135,6 +160,7 @@ public final class HostingRecord {
mIsTopApp = isTopApp;
mDefiningProcessName = definingProcessName;
mAction = action;
+ mTriggerType = triggerType;
}
public @NonNull String getType() {
@@ -188,6 +214,11 @@ public final class HostingRecord {
return mAction;
}
+ /** Returns the type of trigger that led to this process start. */
+ public @NonNull String getTriggerType() {
+ return mTriggerType;
+ }
+
/**
* Creates a HostingRecord for a process that must spawn from the webview zygote
* @param hostingName name of the component to be hosted in this process
@@ -197,7 +228,7 @@ public final class HostingRecord {
String definingPackageName, int definingUid, String definingProcessName) {
return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(),
WEBVIEW_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */,
- definingProcessName, null /* action */);
+ definingProcessName, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
/**
@@ -211,7 +242,7 @@ public final class HostingRecord {
int definingUid, String definingProcessName) {
return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(),
APP_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */,
- definingProcessName, null /* action */);
+ definingProcessName, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
/**
@@ -236,35 +267,49 @@ public final class HostingRecord {
public static int getHostingTypeIdStatsd(@NonNull String hostingType) {
switch(hostingType) {
case HOSTING_TYPE_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ACTIVITY;
case HOSTING_TYPE_ADDED_APPLICATION:
- return ProcessStartTime.HOSTING_TYPE_ADDED_APPLICATION;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ADDED_APPLICATION;
case HOSTING_TYPE_BACKUP:
- return ProcessStartTime.HOSTING_TYPE_BACKUP;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BACKUP;
case HOSTING_TYPE_BROADCAST:
- return ProcessStartTime.HOSTING_TYPE_BROADCAST;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BROADCAST;
case HOSTING_TYPE_CONTENT_PROVIDER:
- return ProcessStartTime.HOSTING_TYPE_CONTENT_PROVIDER;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_CONTENT_PROVIDER;
case HOSTING_TYPE_LINK_FAIL:
- return ProcessStartTime.HOSTING_TYPE_LINK_FAIL;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_LINK_FAIL;
case HOSTING_TYPE_ON_HOLD:
- return ProcessStartTime.HOSTING_TYPE_ON_HOLD;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ON_HOLD;
case HOSTING_TYPE_NEXT_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_NEXT_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_ACTIVITY;
case HOSTING_TYPE_NEXT_TOP_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_NEXT_TOP_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_TOP_ACTIVITY;
case HOSTING_TYPE_RESTART:
- return ProcessStartTime.HOSTING_TYPE_RESTART;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_RESTART;
case HOSTING_TYPE_SERVICE:
- return ProcessStartTime.HOSTING_TYPE_SERVICE;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE;
case HOSTING_TYPE_SYSTEM:
- return ProcessStartTime.HOSTING_TYPE_SYSTEM;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM;
case HOSTING_TYPE_TOP_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_TOP_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY;
case HOSTING_TYPE_EMPTY:
- return ProcessStartTime.HOSTING_TYPE_EMPTY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_EMPTY;
+ default:
+ return PROCESS_START_TIME__TYPE__UNKNOWN;
+ }
+ }
+
+ /**
+ * Map the string triggerType to enum TriggerType defined in ProcessStartTime proto.
+ * @param triggerType
+ * @return enum TriggerType defined in ProcessStartTime proto
+ */
+ public static int getTriggerTypeForStatsd(@NonNull String triggerType) {
+ switch(triggerType) {
+ case TRIGGER_TYPE_ALARM:
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM;
default:
- return ProcessStartTime.HOSTING_TYPE_UNKNOWN;
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN;
}
}
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index d16fe1240d0c..d4ef638d0818 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -343,6 +343,9 @@ public class AttentionManagerService extends SystemService {
*
* Calling this multiple times for duplicate requests will be no-ops, returning true.
*
+ * TODO(b/239130847): Maintain the proximity state in AttentionManagerService and change this
+ * to a polling API.
+ *
* @return {@code true} if the framework was able to dispatch the request
*/
@VisibleForTesting
@@ -853,9 +856,6 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
private void cancelAndUnbindLocked() {
synchronized (mLock) {
- if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) {
- return;
- }
if (mCurrentAttentionCheck != null) {
cancel();
}
@@ -937,7 +937,7 @@ public class AttentionManagerService extends SystemService {
}
}
- class TestableProximityUpdateCallbackInternal extends ProximityUpdateCallbackInternal {
+ class TestableProximityUpdateCallbackInternal implements ProximityUpdateCallbackInternal {
private double mLastCallbackCode = PROXIMITY_UNKNOWN;
@Override
@@ -1069,6 +1069,7 @@ public class AttentionManagerService extends SystemService {
private void resetStates() {
synchronized (mLock) {
mCurrentProximityUpdate = null;
+ cancelAndUnbindLocked();
}
mComponentName = resolveAttentionService(mContext);
}
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 26a63120d793..5620dc36c971 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -337,7 +337,7 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
boolean hasPublicClients = false;
while (clientIterator.hasNext()) {
RecMonitorClient rmc = clientIterator.next();
- if (rcdb.equals(rmc.mDispatcherCb)) {
+ if (rcdb.asBinder().equals(rmc.mDispatcherCb.asBinder())) {
rmc.release();
clientIterator.remove();
} else {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 74ee6800eb63..4f3fd6409cd8 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -66,6 +66,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -708,8 +709,8 @@ public class DisplayDeviceConfig {
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
String suffixFormat, long idNumber) {
- final String suffix = String.format(suffixFormat, idNumber);
- final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
+ final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
+ final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 90e4596af420..6f3a0c516a5b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1328,9 +1328,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
- if (autoBrightnessAdjustmentChanged) {
- mTemporaryAutoBrightnessAdjustment = Float.NaN;
- }
// Use the autobrightness adjustment override if set.
final float autoBrightnessAdjustment;
@@ -2309,14 +2306,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void handleSettingsChange(boolean userSwitch) {
mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+ mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
if (userSwitch) {
// Don't treat user switches as user initiated change.
setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ updateAutoBrightnessAdjustment();
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.resetShortTermModel();
}
}
- mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
// We don't bother with a pending variable for VR screen brightness since we just
// immediately adapt to it.
mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
@@ -2385,6 +2383,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment;
mPendingAutoBrightnessAdjustment = Float.NaN;
+ mTemporaryAutoBrightnessAdjustment = Float.NaN;
return true;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e798adf1c22a..72f3850891ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6423,6 +6423,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Override
public void setVisibilityLogging(String packageName, boolean enable) {
+ PackageManagerServiceUtils.enforceSystemOrRootOrShell(
+ "Only the system or shell can set visibility logging.");
final PackageStateInternal packageState =
snapshot().getPackageStateInternal(packageName);
if (packageState == null) {
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
index af2b902c97e8..8582f5424321 100644
--- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -23,11 +23,8 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
@@ -35,12 +32,11 @@ import android.hardware.biometrics.BiometricStateListener;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
-import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
+import android.view.View;
+import android.view.Window;
import android.view.WindowManager;
import com.android.internal.R;
@@ -48,49 +44,48 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
/**
* Defines behavior for handling interactions between power button events and fingerprint-related
* operations, for devices where the fingerprint sensor (side fps) lives on the power button.
*/
-public class SideFpsEventHandler {
+public class SideFpsEventHandler implements View.OnClickListener {
private static final int DEBOUNCE_DELAY_MILLIS = 500;
- private int getTapWaitForPowerDuration(Context context) {
- int tap = context.getResources().getInteger(
- R.integer.config_sidefpsEnrollPowerPressWindow);
- if (Build.isDebuggable()) {
- tap = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW, tap,
- UserHandle.USER_CURRENT);
- }
- return tap;
- }
-
private static final String TAG = "SideFpsEventHandler";
- @NonNull private final Context mContext;
- @NonNull private final Handler mHandler;
- @NonNull private final PowerManager mPowerManager;
- @NonNull private final Supplier<AlertDialog.Builder> mDialogSupplier;
- @NonNull private final AtomicBoolean mSideFpsEventHandlerReady;
-
- @Nullable private Dialog mDialog;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final PowerManager mPowerManager;
+ @NonNull
+ private final AtomicBoolean mSideFpsEventHandlerReady;
+ private final int mDismissDialogTimeout;
+ @Nullable
+ private SideFpsToast mDialog;
private final Runnable mTurnOffDialog =
() -> {
dismissDialog("mTurnOffDialog");
};
-
- @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener;
-
private @BiometricStateListener.State int mBiometricState;
- private final int mTapWaitForPowerDuration;
private FingerprintManager mFingerprintManager;
+ private DialogProvider mDialogProvider;
+ private long mLastPowerPressTime;
- SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager) {
- this(context, handler, powerManager, () -> new AlertDialog.Builder(context));
+ SideFpsEventHandler(
+ Context context,
+ Handler handler,
+ PowerManager powerManager) {
+ this(context, handler, powerManager, (ctx) -> {
+ SideFpsToast dialog = new SideFpsToast(ctx);
+ dialog.getWindow()
+ .setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ return dialog;
+ });
}
@VisibleForTesting
@@ -98,23 +93,13 @@ public class SideFpsEventHandler {
Context context,
Handler handler,
PowerManager powerManager,
- Supplier<AlertDialog.Builder> dialogSupplier) {
+ DialogProvider provider) {
mContext = context;
mHandler = handler;
mPowerManager = powerManager;
- mDialogSupplier = dialogSupplier;
mBiometricState = STATE_IDLE;
mSideFpsEventHandlerReady = new AtomicBoolean(false);
- mDialogDismissListener =
- (dialog) -> {
- if (mDialog == dialog) {
- if (mHandler != null) {
- mHandler.removeCallbacks(mTurnOffDialog);
- }
- mDialog = null;
- }
- };
-
+ mDialogProvider = provider;
// ensure dialog is dismissed if screen goes off for unrelated reasons
context.registerReceiver(
new BroadcastReceiver() {
@@ -127,7 +112,13 @@ public class SideFpsEventHandler {
}
},
new IntentFilter(Intent.ACTION_SCREEN_OFF));
- mTapWaitForPowerDuration = getTapWaitForPowerDuration(context);
+ mDismissDialogTimeout = context.getResources().getInteger(
+ R.integer.config_sideFpsToastTimeout);
+ }
+
+ @Override
+ public void onClick(View v) {
+ goToSleep(mLastPowerPressTime);
}
/**
@@ -165,8 +156,9 @@ public class SideFpsEventHandler {
Log.v(TAG, "Detected a tap to turn off dialog, ignoring");
mHandler.removeCallbacks(mTurnOffDialog);
}
+ showDialog(eventTime, "Enroll Power Press");
+ mHandler.postDelayed(mTurnOffDialog, mDismissDialogTimeout);
});
- showDialog(eventTime, "Enroll Power Press");
return true;
case STATE_BP_AUTH:
return true;
@@ -176,54 +168,11 @@ public class SideFpsEventHandler {
}
}
- @NonNull
- private static Dialog showConfirmDialog(
- @NonNull AlertDialog.Builder dialogBuilder,
- @NonNull PowerManager powerManager,
- long eventTime,
- @BiometricStateListener.State int biometricState,
- @NonNull DialogInterface.OnDismissListener dismissListener) {
- final boolean enrolling = biometricState == STATE_ENROLLING;
- final int title =
- enrolling
- ? R.string.fp_power_button_enrollment_title
- : R.string.fp_power_button_bp_title;
- final int message =
- enrolling
- ? R.string.fp_power_button_enrollment_message
- : R.string.fp_power_button_bp_message;
- final int positiveText =
- enrolling
- ? R.string.fp_power_button_enrollment_positive_button
- : R.string.fp_power_button_bp_positive_button;
- final int negativeText =
- enrolling
- ? R.string.fp_power_button_enrollment_negative_button
- : R.string.fp_power_button_bp_negative_button;
-
- final Dialog confirmScreenOffDialog =
- dialogBuilder
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(
- positiveText,
- (dialog, which) -> {
- dialog.dismiss();
- powerManager.goToSleep(
- eventTime,
- PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
- 0 /* flags */);
- })
- .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss())
- .setOnDismissListener(dismissListener)
- .setCancelable(false)
- .create();
- confirmScreenOffDialog
- .getWindow()
- .setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
- confirmScreenOffDialog.show();
-
- return confirmScreenOffDialog;
+ private void goToSleep(long eventTime) {
+ mPowerManager.goToSleep(
+ eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+ 0 /* flags */);
}
/**
@@ -247,7 +196,8 @@ public class SideFpsEventHandler {
if (fingerprintManager.isPowerbuttonFps()) {
fingerprintManager.registerBiometricStateListener(
new BiometricStateListener() {
- @Nullable private Runnable mStateRunnable = null;
+ @Nullable
+ private Runnable mStateRunnable = null;
@Override
public void onStateChanged(
@@ -281,13 +231,6 @@ public class SideFpsEventHandler {
public void onBiometricAction(
@BiometricStateListener.Action int action) {
Log.d(TAG, "onBiometricAction " + action);
- switch (action) {
- case BiometricStateListener.ACTION_SENSOR_TOUCH:
- mHandler.postDelayed(
- mTurnOffDialog,
- mTapWaitForPowerDuration);
- break;
- }
}
});
mSideFpsEventHandlerReady.set(true);
@@ -309,12 +252,13 @@ public class SideFpsEventHandler {
Log.d(TAG, "Ignoring show dialog");
return;
}
- mDialog =
- showConfirmDialog(
- mDialogSupplier.get(),
- mPowerManager,
- time,
- mBiometricState,
- mDialogDismissListener);
+ mDialog = mDialogProvider.provideDialog(mContext);
+ mLastPowerPressTime = time;
+ mDialog.show();
+ mDialog.setOnClickListener(this);
+ }
+
+ interface DialogProvider {
+ SideFpsToast provideDialog(Context context);
}
-}
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/policy/SideFpsToast.java b/services/core/java/com/android/server/policy/SideFpsToast.java
new file mode 100644
index 000000000000..db074670de9b
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SideFpsToast.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import com.android.internal.R;
+
+/**
+ * Toast for side fps. This is typically shown during enrollment
+ * when a user presses the power button.
+ *
+ * This dialog is used by {@link SideFpsEventHandler}
+ */
+public class SideFpsToast extends Dialog {
+
+ SideFpsToast(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.side_fps_toast);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ final Window window = this.getWindow();
+ WindowManager.LayoutParams windowParams = window.getAttributes();
+ windowParams.dimAmount = 0;
+ windowParams.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+ windowParams.gravity = Gravity.BOTTOM;
+ window.setAttributes(windowParams);
+ }
+
+ /**
+ * Sets the onClickListener for the toast dialog.
+ * @param listener
+ */
+ public void setOnClickListener(View.OnClickListener listener) {
+ final Button turnOffScreen = findViewById(R.id.turn_off_screen);
+ if (turnOffScreen != null) {
+ turnOffScreen.setOnClickListener(listener);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8de556ac4667..0332935348e9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -489,6 +489,7 @@ class Task extends TaskFragment {
static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
private int mForceHiddenFlags = 0;
+ private boolean mForceTranslucent = false;
// TODO(b/160201781): Revisit double invocation issue in Task#removeChild.
/**
@@ -4342,6 +4343,10 @@ class Task extends TaskFragment {
return true;
}
+ void setForceTranslucent(boolean set) {
+ mForceTranslucent = set;
+ }
+
@Override
public boolean isAlwaysOnTop() {
return !isForceHidden() && super.isAlwaysOnTop();
@@ -4360,6 +4365,11 @@ class Task extends TaskFragment {
}
@Override
+ protected boolean isForceTranslucent() {
+ return mForceTranslucent;
+ }
+
+ @Override
long getProtoFieldId() {
return TASK;
}
@@ -5333,7 +5343,7 @@ class Task extends TaskFragment {
abort = true;
}
if (abort) {
- Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+ android.util.EventLog.writeEvent(0x534e4554, "238605611", callingUid, "");
foundParentInTask = false;
} else {
parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index e6afdd6c5876..c1f06e442294 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -740,6 +740,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
+ protected boolean isForceTranslucent() {
+ return false;
+ }
+
boolean isLeafTaskFragment() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).asTaskFragment() != null) {
@@ -865,7 +869,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
*/
@VisibleForTesting
boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
+ if (!isAttached() || isForceHidden() || isForceTranslucent()) {
return true;
}
final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 95db746698d5..3b9cd368a93b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -646,6 +646,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
+ if ((c.getChangeMask()
+ & WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT) != 0) {
+ tr.setForceTranslucent(c.getForceTranslucent());
+ effects = TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
tr.setActivityWindowingMode(childWindowingMode);
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index 2219d477630e..e2f56ba56f3d 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -301,6 +301,35 @@ public class BackupManagerServiceRoboTest {
verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
}
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsUserReadyForBackup_withoutPermission_throwsSecurityException() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.isUserReadyForBackup(mUserOneId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsUserReadyForBackup_withPermission_callsMethodForUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserSystemService);
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+ assertThat(backupManagerService.isUserReadyForBackup(mUserOneId)).isTrue();
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
index 842b23c91e41..4a16874c7acf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
@@ -17,13 +17,13 @@
package com.android.server.accessibility;
-import static android.view.accessibility.AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
import static org.junit.Assert.assertEquals;
@@ -528,7 +528,8 @@ public class AccessibilityInteractionControllerNodeRequestsTest {
// different client that holds different fetch flags for TextView1.
sendNodeRequestToController(nodeId, mMockClientCallback2,
mMockClient2InteractionId,
- FLAG_PREFETCH_SIBLINGS | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ FLAG_PREFETCH_SIBLINGS
+ | FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
| FLAG_PREFETCH_ANCESTORS);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
index 7746bd6859d6..0635cc4239f4 100644
--- a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.AlertDialog;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.fingerprint.FingerprintManager;
@@ -36,10 +35,13 @@ import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableResources;
import android.view.Window;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -47,7 +49,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.util.List;
@@ -68,17 +69,22 @@ public class SideFpsEventHandlerTest {
BiometricStateListener.STATE_BP_AUTH,
BiometricStateListener.STATE_AUTH_OTHER);
+ private static final Integer AUTO_DISMISS_DIALOG = 500;
+
@Rule
public TestableContext mContext =
new TestableContext(InstrumentationRegistry.getContext(), null);
- @Mock private PackageManager mPackageManager;
- @Mock private FingerprintManager mFingerprintManager;
- @Spy private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext);
- @Mock private AlertDialog mAlertDialog;
- @Mock private Window mWindow;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private SideFpsToast mDialog;
+ @Mock
+ private Window mWindow;
- private TestLooper mLooper = new TestLooper();
+ private final TestLooper mLooper = new TestLooper();
private SideFpsEventHandler mEventHandler;
private BiometricStateListener mBiometricStateListener;
@@ -88,16 +94,17 @@ public class SideFpsEventHandlerTest {
mContext.addMockSystemService(PackageManager.class, mPackageManager);
mContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+ TestableResources resources = mContext.getOrCreateTestableResources();
+ resources.addOverride(R.integer.config_sideFpsToastTimeout, AUTO_DISMISS_DIALOG);
- when(mDialogBuilder.create()).thenReturn(mAlertDialog);
- when(mAlertDialog.getWindow()).thenReturn(mWindow);
+ when(mDialog.getWindow()).thenReturn(mWindow);
mEventHandler =
new SideFpsEventHandler(
mContext,
new Handler(mLooper.getLooper()),
mContext.getSystemService(PowerManager.class),
- () -> mDialogBuilder);
+ (ctx) -> mDialog);
}
@Test
@@ -108,7 +115,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
@@ -120,7 +127,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(200L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
}
@@ -133,7 +140,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(400L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
}
@@ -148,7 +155,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(90000L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
@@ -159,18 +166,18 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
- public void promptsWhenBPisActive() throws Exception {
+ public void doesNotpromptWhenBPisActive() throws Exception {
setupWithSensor(true /* hasSideFps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_BP_AUTH);
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
@@ -181,57 +188,40 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
- verify(mAlertDialog, never()).dismiss();
+ verify(mDialog).show();
}
@Test
- public void dismissesDialogOnTouchWhenEnrolling() throws Exception {
+ public void dialogDismissesAfterTime() throws Exception {
setupWithSensor(true /* hasSfps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_ENROLLING);
- when(mAlertDialog.isShowing()).thenReturn(true);
+ when(mDialog.isShowing()).thenReturn(true);
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
-
- mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- mLooper.moveTimeForward(10000);
+ verify(mDialog).show();
+ mLooper.moveTimeForward(AUTO_DISMISS_DIALOG);
mLooper.dispatchAll();
-
- verify(mAlertDialog).dismiss();
+ verify(mDialog).dismiss();
}
@Test
- public void dismissesDialogFailsWhenPowerPressedAndDialogShowing() throws Exception {
+ public void dialogDoesNotDismissOnSensorTouch() throws Exception {
setupWithSensor(true /* hasSfps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_ENROLLING);
- when(mAlertDialog.isShowing()).thenReturn(true);
+ when(mDialog.isShowing()).thenReturn(true);
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
+ verify(mDialog).show();
mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue();
-
+ mLooper.moveTimeForward(AUTO_DISMISS_DIALOG - 1);
mLooper.dispatchAll();
- verify(mAlertDialog, never()).dismiss();
- }
- @Test
- public void showDialogAfterTap() throws Exception {
- setupWithSensor(true /* hasSfps */, true /* initialized */);
-
- setBiometricState(BiometricStateListener.STATE_ENROLLING);
- when(mAlertDialog.isShowing()).thenReturn(true);
- mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue();
-
- mLooper.dispatchAll();
- verify(mAlertDialog).show();
+ verify(mDialog, never()).dismiss();
}
private void setBiometricState(@BiometricStateListener.State int newState) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 69ba8ad58c7c..47ff3d584907 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -79,6 +79,8 @@ public class ContentRecorderTests extends WindowTestsBase {
private ConfigListener mConfigListener;
private CountDownLatch mLatch;
+ private VirtualDisplay mVirtualDisplay;
+
@Before public void setUp() {
// GIVEN SurfaceControl can successfully mirror the provided surface.
sSurfaceSize = new Point(
@@ -89,10 +91,10 @@ public class ContentRecorderTests extends WindowTestsBase {
doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
// GIVEN the VirtualDisplay associated with the session (so the display has state ON).
- VirtualDisplay virtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
+ mVirtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
sSurfaceSize.x, sSurfaceSize.y,
DisplayMetrics.DENSITY_140, new Surface(), VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
- final int displayId = virtualDisplay.getDisplay().getDisplayId();
+ final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
mWm.mRoot.onDisplayAdded(displayId);
final DisplayContent virtualDisplayContent = mWm.mRoot.getDisplayContent(displayId);
mContentRecorder = new ContentRecorder(virtualDisplayContent);
@@ -119,6 +121,8 @@ public class ContentRecorderTests extends WindowTestsBase {
@After
public void teardown() {
DeviceConfig.removeOnPropertiesChangedListener(mConfigListener);
+ mVirtualDisplay.release();
+ mWm.mRoot.onDisplayRemoved(mVirtualDisplay.getDisplay().getDisplayId());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 969353216d20..a62625cea46c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -578,6 +578,22 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testContainerTranslucentChanges() {
+ removeGlobalMinSizeRestriction();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertFalse(rootTask.isTranslucent(activity));
+ t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), true);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+ assertTrue(rootTask.isTranslucent(activity));
+ t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), false);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+ assertFalse(rootTask.isTranslucent(activity));
+ }
+
+ @Test
public void testSetIgnoreOrientationRequest_taskDisplayArea() {
removeGlobalMinSizeRestriction();
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 85b9f759beac..9562c1a5bcf9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -508,6 +508,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// current USB state
private boolean mHostConnected;
private boolean mUsbAccessoryConnected;
+ private boolean mInHostModeWithNoAccessoryConnected;
private boolean mSourcePower;
private boolean mSinkPower;
private boolean mConfigured;
@@ -959,6 +960,17 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mSupportsAllCombinations = false;
}
+ if (mHostConnected) {
+ if (!mUsbAccessoryConnected) {
+ mInHostModeWithNoAccessoryConnected = true;
+ } else {
+ mInHostModeWithNoAccessoryConnected = false;
+ }
+ } else {
+ // if not in host mode, reset value to false
+ mInHostModeWithNoAccessoryConnected = false;
+ }
+
mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
args.recycle();
@@ -983,6 +995,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
Slog.i(TAG, "HOST_STATE connected:" + mUsbAccessoryConnected);
}
+ if (!devices.hasNext()) {
+ mInHostModeWithNoAccessoryConnected = true;
+ } else {
+ mInHostModeWithNoAccessoryConnected = false;
+ }
+
mHideUsbNotification = false;
while (devices.hasNext()) {
Map.Entry pair = (Map.Entry) devices.next();
@@ -1192,7 +1210,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// Dont show the notification when connected to a USB peripheral
// and the link does not support PR_SWAP and DR_SWAP
- if (mHideUsbNotification && !mSupportsAllCombinations) {
+ if ((mHideUsbNotification || mInHostModeWithNoAccessoryConnected)
+ && !mSupportsAllCombinations) {
if (mUsbNotificationId != 0) {
mNotificationManager.cancelAsUser(null, mUsbNotificationId,
UserHandle.ALL);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 1e0e8366c385..2eae68bd3182 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -18,6 +18,8 @@ package com.android.server.voiceinteraction;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO;
+import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
+import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS;
@@ -58,6 +60,7 @@ import static com.android.server.voiceinteraction.SoundTriggerSessionPermissions
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.attention.AttentionManagerInternal;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -186,6 +189,12 @@ final class HotwordDetectionConnection {
final int mUser;
final Context mContext;
+ final AttentionManagerInternal mAttentionManagerInternal;
+
+ final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal =
+ this::setProximityMeters;
+
+
volatile HotwordDetectionServiceIdentity mIdentity;
private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback;
private Instant mLastRestartInstant;
@@ -206,6 +215,8 @@ final class HotwordDetectionConnection {
private @NonNull ServiceConnection mRemoteHotwordDetectionService;
private IBinder mAudioFlinger;
private boolean mDebugHotwordLogging = false;
+ @GuardedBy("mLock")
+ private double mProximityMeters = PROXIMITY_UNKNOWN;
HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity, ComponentName serviceName, int userId,
@@ -233,6 +244,8 @@ final class HotwordDetectionConnection {
mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked();
+ mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class);
+ mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal);
mLastRestartInstant = Instant.now();
updateStateAfterProcessStart(options, sharedMemory);
@@ -397,6 +410,7 @@ final class HotwordDetectionConnection {
if (mAudioFlinger != null) {
mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
}
+ mAttentionManagerInternal.onStopProximityUpdates(mProximityCallbackInternal);
}
void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
@@ -464,6 +478,7 @@ final class HotwordDetectionConnection {
mSoftwareCallback.onError();
return;
}
+ saveProximityMetersToBundle(result);
mSoftwareCallback.onDetected(result, null, null);
if (result != null) {
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
@@ -591,6 +606,7 @@ final class HotwordDetectionConnection {
externalCallback.onError(CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION);
return;
}
+ saveProximityMetersToBundle(result);
externalCallback.onKeyphraseDetected(recognitionEvent, result);
if (result != null) {
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
@@ -1159,6 +1175,20 @@ final class HotwordDetectionConnection {
});
}
+ private void saveProximityMetersToBundle(HotwordDetectedResult result) {
+ synchronized (mLock) {
+ if (result != null && mProximityMeters != PROXIMITY_UNKNOWN) {
+ result.getExtras().putDouble(EXTRA_PROXIMITY_METERS, mProximityMeters);
+ }
+ }
+ }
+
+ private void setProximityMeters(double proximityMeters) {
+ synchronized (mLock) {
+ mProximityMeters = proximityMeters;
+ }
+ }
+
private static void bestEffortClose(Closeable... closeables) {
for (Closeable closeable : closeables) {
bestEffortClose(closeable);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1f97d862fb6b..f28408e1abc9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -194,13 +194,24 @@ public class SubscriptionManager {
}
@Override
- public T recompute(Void aVoid) {
+ public T recompute(Void query) {
+ // This always throws on any error. The exceptions must be handled outside
+ // the cache.
+ try {
+ return mInterfaceMethod.applyOrThrow(TelephonyManager.getSubscriptionService());
+ } catch (Exception re) {
+ throw new RuntimeException(re);
+ }
+ }
+
+ @Override
+ public T query(Void query) {
T result = mDefaultValue;
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- result = mInterfaceMethod.applyOrThrow(iSub);
+ result = super.query(query);
}
} catch (Exception ex) {
Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty);
@@ -229,12 +240,24 @@ public class SubscriptionManager {
@Override
public T recompute(Integer query) {
+ // This always throws on any error. The exceptions must be handled outside
+ // the cache.
+ try {
+ return mInterfaceMethod.applyOrThrow(
+ TelephonyManager.getSubscriptionService(), query);
+ } catch (Exception re) {
+ throw new RuntimeException(re);
+ }
+ }
+
+ @Override
+ public T query(Integer query) {
T result = mDefaultValue;
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- result = mInterfaceMethod.applyOrThrow(iSub, query);
+ result = super.query(query);
}
} catch (Exception ex) {
Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty);
@@ -4118,4 +4141,3 @@ public class SubscriptionManager {
usageSetting, subscriptionId, mContext.getOpPackageName()));
}
}
-
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index cd07b1ebbf92..46a846bce35f 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -641,7 +641,23 @@ class PackageFlattener {
local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
} else {
// resource isn't exempt from collapse, add it as obfuscated value
- local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
+ if (entry.overlayable_item) {
+ // if the resource name of the specific entry is obfuscated and this
+ // entry is in the overlayable list, the overlay can't work on this
+ // overlayable at runtime because the name has been obfuscated in
+ // resources.arsc during flatten operation.
+ const OverlayableItem& item = entry.overlayable_item.value();
+ context_->GetDiagnostics()->Warn(android::DiagMessage(item.overlayable->source)
+ << "The resource name of overlayable entry "
+ << resource_name.to_string() << "'"
+ << " shouldn't be obfuscated in resources.arsc");
+
+ local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
+ } else {
+ // TODO(b/228192695): output the entry.name and Resource id to make
+ // de-obfuscated possible.
+ local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
+ }
}
// Group values by configuration.
for (auto& config_value : entry.values) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 1dd2468b5868..e48fca61fef8 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -878,4 +878,52 @@ TEST_F(TableFlattenerTest, FlattenCustomResourceTypes) {
Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
}
+TEST_F(TableFlattenerTest, FlattenTypeEntryWithNameCollapseNotInExemption) {
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+
+ std::string name = "com.app.test:color/overlayable_color";
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("com.app.test:color/overlayable_color", ResourceId(0x7f010000),
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_COLOR_ARGB8),
+ 0xffaabbcc))
+ .SetOverlayable(name, overlayable_item)
+ .Build();
+
+ TableFlattenerOptions options;
+ options.collapse_key_stringpool = true;
+
+ ResTable res_table;
+ EXPECT_THAT(Flatten(context_.get(), options, table.get(), &res_table), testing::IsTrue());
+ EXPECT_THAT(Exists(&res_table, "com.app.test:color/overlayable_color", ResourceId(0x7f010000), {},
+ Res_value::TYPE_INT_COLOR_ARGB8, 0xffaabbcc, 0u),
+ testing::IsTrue());
+}
+
+TEST_F(TableFlattenerTest, FlattenTypeEntryWithNameCollapseInExemption) {
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+
+ std::string name = "com.app.test:color/overlayable_color";
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("com.app.test:color/overlayable_color", ResourceId(0x7f010000),
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_COLOR_ARGB8),
+ 0xffaabbcc))
+ .SetOverlayable(name, overlayable_item)
+ .Build();
+
+ TableFlattenerOptions options;
+ options.collapse_key_stringpool = true;
+ options.name_collapse_exemptions.insert(
+ ResourceName({}, ResourceType::kColor, "overlayable_color"));
+
+ ResTable res_table;
+ EXPECT_THAT(Flatten(context_.get(), options, table.get(), &res_table), testing::IsTrue());
+ EXPECT_THAT(Exists(&res_table, "com.app.test:color/overlayable_color", ResourceId(0x7f010000), {},
+ Res_value::TYPE_INT_COLOR_ARGB8, 0xffaabbcc, 0u),
+ testing::IsTrue());
+}
+
} // namespace aapt