Merge "updating the red color brightness for emergency items"
diff --git a/config/generate-preloaded-classes.sh b/config/generate-preloaded-classes.sh
deleted file mode 100755
index b17a366..0000000
--- a/config/generate-preloaded-classes.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-if [ "$#" -lt 2 ]; then
- echo "Usage $0 <input classes file> <denylist file> [extra classes files]"
- exit 1
-fi
-
-# Write file headers first
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-cat "$DIR/copyright-header"
-echo "# Preloaded-classes filter file for phones.
-#
-# Classes in this file will be allocated into the boot image, and forcibly initialized in
-# the zygote during initialization. This is a trade-off, using virtual address space to share
-# common heap between apps.
-#
-# This file has been derived for mainline phone (and tablet) usage.
-#"
-
-input=$1
-denylist=$2
-shift 2
-extra_classes_files=("$@")
-
-# Disable locale to enable lexicographical sorting
-LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$denylist" -v -F -x | grep -v "\$NoPreloadHolder"
diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra
deleted file mode 100644
index 09f393a..0000000
--- a/config/preloaded-classes-extra
+++ /dev/null
@@ -1,14 +0,0 @@
-android.icu.impl.coll.CollationRoot
-android.icu.impl.IDNA2003
-android.icu.impl.number.Parse
-android.icu.util.TimeZone
-android.media.ImageReader
-android.media.MediaCodecList
-android.media.MediaPlayer
-android.media.SoundPool
-android.text.format.Formatter
-android.text.Html$HtmlParser
-android.util.Log$PreloadHolder
-com.android.org.conscrypt.TrustedCertificateStore
-org.ccil.cowan.tagsoup.HTMLScanner
-sun.security.jca.Providers
diff --git a/core/api/current.txt b/core/api/current.txt
index 82e170d..d7b17b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3255,18 +3255,18 @@
}
public final class TouchInteractionController {
- method public void addListener(@Nullable java.util.concurrent.Executor, @NonNull android.accessibilityservice.TouchInteractionController.Listener);
method public int getDisplayId();
method public int getMaxPointerCount();
method public int getState();
method public void performClick();
method public void performLongClickAndStartDrag();
- method public void removeAllListeners();
- method public boolean removeListener(@NonNull android.accessibilityservice.TouchInteractionController.Listener);
+ method public void registerCallback(@Nullable java.util.concurrent.Executor, @NonNull android.accessibilityservice.TouchInteractionController.Callback);
method public void requestDelegating();
method public void requestDragging(int);
method public void requestTouchExploration();
method @NonNull public static String stateToString(int);
+ method public void unregisterAllCallbacks();
+ method public boolean unregisterCallback(@NonNull android.accessibilityservice.TouchInteractionController.Callback);
field public static final int STATE_CLEAR = 0; // 0x0
field public static final int STATE_DELEGATING = 4; // 0x4
field public static final int STATE_DRAGGING = 3; // 0x3
@@ -3274,7 +3274,7 @@
field public static final int STATE_TOUCH_INTERACTING = 1; // 0x1
}
- public static interface TouchInteractionController.Listener {
+ public static interface TouchInteractionController.Callback {
method public void onMotionEvent(@NonNull android.view.MotionEvent);
method public void onStateChanged(int);
}
@@ -31666,9 +31666,9 @@
method public byte[] marshall();
method @NonNull public static android.os.Parcel obtain();
method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
- method @Nullable public Object[] readArray(@Nullable ClassLoader);
+ method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>);
- method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
+ method @Deprecated @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>);
method public void readBinderArray(@NonNull android.os.IBinder[]);
method public void readBinderList(@NonNull java.util.List<android.os.IBinder>);
@@ -31686,32 +31686,32 @@
method public android.os.ParcelFileDescriptor readFileDescriptor();
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
- method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
+ method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>);
method public int readInt();
method public void readIntArray(@NonNull int[]);
method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>);
method public <T extends android.os.IInterface> void readInterfaceList(@NonNull java.util.List<T>, @NonNull java.util.function.Function<android.os.IBinder,T>);
- method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
+ method @Deprecated public void readList(@NonNull java.util.List, @Nullable ClassLoader);
method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
method public long readLong();
method public void readLongArray(@NonNull long[]);
- method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
+ method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
- method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
+ method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
- method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
+ method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
- method @Nullable public java.io.Serializable readSerializable();
+ method @Deprecated @Nullable public java.io.Serializable readSerializable();
method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public android.util.Size readSize();
method @NonNull public android.util.SizeF readSizeF();
- method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
+ method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>);
method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray();
method @Nullable public String readString();
@@ -42743,6 +42743,8 @@
method @NonNull public java.util.List<android.net.Uri> getDeviceToDeviceStatusSharingContacts(int);
method public int getDeviceToDeviceStatusSharingPreference(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_NUMBERS, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public String getPhoneNumber(int, int);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_NUMBERS, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public String getPhoneNumber(int);
method public static int getSlotIndex(int);
method @Nullable public int[] getSubscriptionIds(int);
method @NonNull public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
@@ -42754,6 +42756,7 @@
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
+ method public void setCarrierPhoneNumber(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingContacts(int, @NonNull java.util.List<android.net.Uri>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingPreference(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
@@ -42780,6 +42783,9 @@
field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
field public static final int INVALID_SIM_SLOT_INDEX = -1; // 0xffffffff
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
+ field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
+ field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
+ field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
old mode 100755
new mode 100644
index 4d6c1c8..a6f9276
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8677,6 +8677,28 @@
method public boolean hasSingleFileDescriptor();
}
+ public final class NewUserRequest {
+ method @Nullable public String getName();
+ method @NonNull public String getUserType();
+ method public boolean isAdmin();
+ method public boolean isEphemeral();
+ }
+
+ public static final class NewUserRequest.Builder {
+ ctor public NewUserRequest.Builder();
+ method @NonNull public android.os.NewUserRequest build();
+ method @NonNull public android.os.NewUserRequest.Builder setAdmin();
+ method @NonNull public android.os.NewUserRequest.Builder setEphemeral();
+ method @NonNull public android.os.NewUserRequest.Builder setName(@Nullable String);
+ method @NonNull public android.os.NewUserRequest.Builder setUserType(@NonNull String);
+ }
+
+ public final class NewUserResponse {
+ method public int getOperationResult();
+ method @Nullable public android.os.UserHandle getUser();
+ method public boolean isSuccessful();
+ }
+
public interface Parcelable {
field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
@@ -8913,6 +8935,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean canHaveRestrictedProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.NewUserResponse createUser(@NonNull android.os.NewUserRequest);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index b435acf..716f43e 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -31,6 +31,10 @@
+MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setAdmin():
+ android.os.NewUserRequest does not declare a `getAdmin()` method matching method android.os.NewUserRequest.Builder.setAdmin()
+MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setEphemeral():
+ android.os.NewUserRequest does not declare a `getEphemeral()` method matching method android.os.NewUserRequest.Builder.setEphemeral()
MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUid(int):
android.security.keystore.KeyGenParameterSpec does not declare a `getUid()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUid(int)
MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index d9be49a..a8ba1d3 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -100,8 +100,8 @@
private final Object mLock;
private final int mDisplayId;
private boolean mServiceDetectsGestures;
- /** Map of listeners to executors. Lazily created when adding the first listener. */
- private ArrayMap<Listener, Executor> mListeners;
+ /** Map of callbacks to executors. Lazily created when adding the first callback. */
+ private ArrayMap<Callback, Executor> mCallbacks;
// The current state of the display.
private int mState = STATE_CLEAR;
@@ -114,38 +114,38 @@
}
/**
- * Adds the specified change listener to the list of motion event listeners. The callback will
+ * Adds the specified callback to the list of callbacks. The callback will
* run using on the specified {@link Executor}', or on the service's main thread if the
* Executor is {@code null}.
- * @param listener the listener to add, must be non-null
+ * @param callback the callback to add, must be non-null
* @param executor the executor for this callback, or {@code null} to execute on the service's
* main thread
*/
- public void addListener(@Nullable Executor executor, @NonNull Listener listener) {
+ public void registerCallback(@Nullable Executor executor, @NonNull Callback callback) {
synchronized (mLock) {
- if (mListeners == null) {
- mListeners = new ArrayMap<>();
+ if (mCallbacks == null) {
+ mCallbacks = new ArrayMap<>();
}
- mListeners.put(listener, executor);
- if (mListeners.size() == 1) {
+ mCallbacks.put(callback, executor);
+ if (mCallbacks.size() == 1) {
setServiceDetectsGestures(true);
}
}
}
/**
- * Removes the specified listener from the list of motion event listeners.
+ * Unregisters the specified callback.
*
- * @param listener the listener to remove, must be non-null
- * @return {@code true} if the listener was removed, {@code false} otherwise
+ * @param callback the callback to remove, must be non-null
+ * @return {@code true} if the callback was removed, {@code false} otherwise
*/
- public boolean removeListener(@NonNull Listener listener) {
- if (mListeners == null) {
+ public boolean unregisterCallback(@NonNull Callback callback) {
+ if (mCallbacks == null) {
return false;
}
synchronized (mLock) {
- boolean result = mListeners.remove(listener) != null;
- if (result && mListeners.size() == 0) {
+ boolean result = mCallbacks.remove(callback) != null;
+ if (result && mCallbacks.size() == 0) {
setServiceDetectsGestures(false);
}
return result;
@@ -153,60 +153,60 @@
}
/**
- * Removes all listeners and returns control of touch interactions to the framework.
+ * Removes all callbacks and returns control of touch interactions to the framework.
*/
- public void removeAllListeners() {
- if (mListeners != null) {
+ public void unregisterAllCallbacks() {
+ if (mCallbacks != null) {
synchronized (mLock) {
- mListeners.clear();
+ mCallbacks.clear();
setServiceDetectsGestures(false);
}
}
}
/**
- * Dispatches motion events to any registered listeners. This should be called on the service's
+ * Dispatches motion events to any registered callbacks. This should be called on the service's
* main thread.
*/
void onMotionEvent(MotionEvent event) {
- final ArrayMap<Listener, Executor> entries;
+ final ArrayMap<Callback, Executor> entries;
synchronized (mLock) {
- // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
+ // callbacks may remove themselves. Perform a shallow copy to avoid concurrent
// modification.
- entries = new ArrayMap<>(mListeners);
+ entries = new ArrayMap<>(mCallbacks);
}
for (int i = 0, count = entries.size(); i < count; i++) {
- final Listener listener = entries.keyAt(i);
+ final Callback callback = entries.keyAt(i);
final Executor executor = entries.valueAt(i);
if (executor != null) {
- executor.execute(() -> listener.onMotionEvent(event));
+ executor.execute(() -> callback.onMotionEvent(event));
} else {
- // We're already on the main thread, just run the listener.
- listener.onMotionEvent(event);
+ // We're already on the main thread, just run the callback.
+ callback.onMotionEvent(event);
}
}
}
/**
- * Dispatches motion events to any registered listeners. This should be called on the service's
+ * Dispatches motion events to any registered callbacks. This should be called on the service's
* main thread.
*/
void onStateChanged(@State int state) {
mState = state;
- final ArrayMap<Listener, Executor> entries;
+ final ArrayMap<Callback, Executor> entries;
synchronized (mLock) {
- // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
+ // callbacks may remove themselves. Perform a shallow copy to avoid concurrent
// modification.
- entries = new ArrayMap<>(mListeners);
+ entries = new ArrayMap<>(mCallbacks);
}
for (int i = 0, count = entries.size(); i < count; i++) {
- final Listener listener = entries.keyAt(i);
+ final Callback callback = entries.keyAt(i);
final Executor executor = entries.valueAt(i);
if (executor != null) {
- executor.execute(() -> listener.onStateChanged(state));
+ executor.execute(() -> callback.onStateChanged(state));
} else {
- // We're already on the main thread, just run the listener.
- listener.onStateChanged(state);
+ // We're already on the main thread, just run the callback.
+ callback.onStateChanged(state);
}
}
}
@@ -238,7 +238,7 @@
/**
* If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at
- * least one listener has been added for this display this function tells the framework to
+ * least one callback has been added for this display this function tells the framework to
* initiate touch exploration. Touch exploration will continue for the duration of this
* interaction.
*/
@@ -259,7 +259,7 @@
/**
* If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
- * one listener has been added, this function tells the framework to initiate a dragging
+ * one callback has been added, this function tells the framework to initiate a dragging
* interaction using the specified pointer. The pointer's movements will be passed through to
* the rest of the input pipeline. Dragging is often used to perform two-finger scrolling.
*
@@ -287,7 +287,7 @@
/**
* If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
- * one listener has been added, this function tells the framework to initiate a delegating
+ * one callback has been added, this function tells the framework to initiate a delegating
* interaction. Motion events will be passed through as-is to the rest of the input pipeline for
* the duration of this interaction.
*/
@@ -308,7 +308,7 @@
/**
* If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
- * one listener has been added, this function tells the framework to perform a click.
+ * one callback has been added, this function tells the framework to perform a click.
* The framework will first try to perform
* {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_CLICK} on the item with
* accessibility focus. If that fails, the framework will simulate a click using motion events
@@ -330,7 +330,7 @@
/**
* If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
* {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
- * one listener has been added, this function tells the framework to perform a long click.
+ * one callback has been added, this function tells the framework to perform a long click.
* The framework will simulate a long click using motion events on the last location with
* accessibility focus and will delegate any movements to the rest of the input pipeline. This
* allows a user to double-tap and hold to trigger a drag and then execute that drag by moving
@@ -350,9 +350,9 @@
}
private void checkState() {
- if (!mServiceDetectsGestures || mListeners.size() == 0) {
+ if (!mServiceDetectsGestures || mCallbacks.size() == 0) {
throw new IllegalStateException(
- "State transitions are not allowed without first adding a listener.");
+ "State transitions are not allowed without first adding a callback.");
}
if (mState != STATE_TOUCH_INTERACTING) {
throw new IllegalStateException(
@@ -402,8 +402,8 @@
}
}
- /** Listeners allow services to receive motion events and state change updates. */
- public interface Listener {
+ /** callbacks allow services to receive motion events and state change updates. */
+ public interface Callback {
/**
* Called when the framework has sent a motion event to the service.
*
diff --git a/core/java/android/app/communal/OWNERS b/core/java/android/app/communal/OWNERS
new file mode 100644
index 0000000..b02883d
--- /dev/null
+++ b/core/java/android/app/communal/OWNERS
@@ -0,0 +1,4 @@
+brycelee@google.com
+justinkoh@google.com
+lusilva@google.com
+xilei@google.com
\ No newline at end of file
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 95892aa..b06d076 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -96,7 +96,7 @@
public void requestState(@NonNull DeviceStateRequest request,
@Nullable @CallbackExecutor Executor executor,
@Nullable DeviceStateRequest.Callback callback) {
- mGlobal.requestState(request, callback, executor);
+ mGlobal.requestState(request, executor, callback);
}
/**
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index 904a54b..85e70b0 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -117,7 +117,7 @@
* @see DeviceStateRequest
*/
public void requestState(@NonNull DeviceStateRequest request,
- @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
if (callback == null && executor != null) {
throw new IllegalArgumentException("Callback must be supplied with executor.");
} else if (executor == null && callback != null) {
@@ -149,7 +149,7 @@
/**
* Cancels a {@link DeviceStateRequest request} previously submitted with a call to
- * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
*
* @see DeviceStateManager#cancelRequest(DeviceStateRequest)
*/
@@ -408,7 +408,7 @@
return;
}
- mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest));
}
}
}
diff --git a/core/java/android/os/NewUserRequest.java b/core/java/android/os/NewUserRequest.java
new file mode 100644
index 0000000..2ebc01f
--- /dev/null
+++ b/core/java/android/os/NewUserRequest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Contains necessary information to create user using
+ * {@link UserManager#createUser(NewUserRequest)}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NewUserRequest {
+ @Nullable
+ private final String mName;
+ private final boolean mAdmin;
+ private final boolean mEphemeral;
+ @NonNull
+ private final String mUserType;
+
+ private NewUserRequest(Builder builder) {
+ mName = builder.mName;
+ mAdmin = builder.mAdmin;
+ mEphemeral = builder.mEphemeral;
+ mUserType = builder.mUserType;
+ }
+
+ /**
+ * Gets the user name.
+ */
+ @Nullable
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Is user Ephemenral?
+ *
+ * <p> Ephemeral user will be removed after leaving the foreground.
+ */
+ public boolean isEphemeral() {
+ return mEphemeral;
+ }
+
+ /**
+ * Is user Admin?
+ *
+ * <p> Admin user is with administrative privileges and such user can create and
+ * delete users.
+ */
+ public boolean isAdmin() {
+ return mAdmin;
+ }
+
+ /**
+ * Gets user type.
+ *
+ * <p> Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and
+ * {@link USER_TYPE_FULL_GUEST}
+ */
+ @NonNull
+ public String getUserType() {
+ return mUserType;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "NewUserRequest- UserName:%s, userType:%s, IsAdmin:%s, IsEphemeral:%s.", mName,
+ mUserType, mAdmin, mEphemeral);
+ }
+
+ /**
+ * Builder for building {@link NewUserRequest}
+ */
+ public static final class Builder {
+
+ private String mName;
+ private boolean mAdmin;
+ private boolean mEphemeral;
+ private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY;
+
+ /**
+ * Sets user name.
+ */
+ @NonNull
+ public Builder setName(@Nullable String name) {
+ mName = name;
+ return this;
+ }
+
+ /**
+ * Sets user as admin.
+ *
+ * <p> Admin user is with administrative privileges and such user can create
+ * and delete users.
+ */
+ @NonNull
+ public Builder setAdmin() {
+ mAdmin = true;
+ return this;
+ }
+
+ /**
+ * Sets user as ephemeral.
+ *
+ * <p> Ephemeral user will be removed after leaving the foreground.
+ */
+ @NonNull
+ public Builder setEphemeral() {
+ mEphemeral = true;
+ return this;
+ }
+
+ /**
+ * Sets user type.
+ * <p>
+ * Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and
+ * {@link UserManager.USER_TYPE_FULL_GUEST}. Default value is
+ * {@link UserManager.USER_TYPE_FULL_SECONDARY}.
+ */
+ @NonNull
+ public Builder setUserType(@NonNull String type) {
+ mUserType = type;
+ return this;
+ }
+
+ /**
+ * Builds {@link NewUserRequest}
+ *
+ * @throws IllegalStateException if builder is configured with incompatible properties and
+ * it is not possible to create such user. For example - a guest admin user.
+ */
+ @NonNull
+ public NewUserRequest build() {
+ checkIfPropertiesAreCompatible();
+ return new NewUserRequest(this);
+ }
+
+ private void checkIfPropertiesAreCompatible() {
+ // Conditions which can't be true simultaneously
+ // A guest user can't be admin user
+ if (mAdmin && mUserType == UserManager.USER_TYPE_FULL_GUEST) {
+ throw new IllegalStateException("A guest user can't be admin.");
+ }
+
+ // check for only supported user types
+ if (mUserType != UserManager.USER_TYPE_FULL_SECONDARY
+ && mUserType != UserManager.USER_TYPE_FULL_GUEST) {
+ throw new IllegalStateException("Unsupported user type: " + mUserType);
+ }
+ }
+ }
+}
diff --git a/core/java/android/os/NewUserResponse.java b/core/java/android/os/NewUserResponse.java
new file mode 100644
index 0000000..3869559
--- /dev/null
+++ b/core/java/android/os/NewUserResponse.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Contains the response of the call {@link UserManager#createUser(NewUserRequest)}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NewUserResponse {
+
+ private final @Nullable UserHandle mUser;
+ private final @UserManager.UserOperationResult int mOperationResult;
+
+ NewUserResponse(@Nullable UserHandle user,
+ @UserManager.UserOperationResult int operationResult) {
+ mUser = user;
+ mOperationResult = operationResult;
+ }
+
+ /**
+ * Is user creation successful?
+ */
+ public boolean isSuccessful() {
+ return mUser != null;
+ }
+
+ // TODO(b/199446283): If UserHandle.NULL is systemAPI, that can be returned here instead of null
+ /**
+ * Gets the created user handle.
+ */
+ public @Nullable UserHandle getUser() {
+ return mUser;
+ }
+
+ /**
+ * Gets operation results.
+ */
+ public @UserManager.UserOperationResult int getOperationResult() {
+ return mOperationResult;
+ }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 09e5a8f..7bdb6b9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2990,7 +2990,12 @@
* Please use {@link #readBundle(ClassLoader)} instead (whose data must have
* been written with {@link #writeBundle}. Read into an existing Map object
* from the parcel at the current dataPosition().
+ *
+ * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this
+ * method is still preferred use the type-safer version {@link #readMap(Map, ClassLoader,
+ * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
int n = readInt();
readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
@@ -3016,7 +3021,14 @@
* Read into an existing List object from the parcel at the current
* dataPosition(), using the given class loader to load any enclosed
* Parcelables. If it is null, the default class loader is used.
+ *
+ * @deprecated Use the type-safer version {@link #readList(List, ClassLoader, Class)} starting
+ * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+ * use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the items'
+ * class is final) since this is also more performant. Note that changing to the latter
+ * also requires changing the writes.
*/
+ @Deprecated
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
int N = readInt();
readListInternal(outVal, N, loader, /* clazz */ null);
@@ -3043,10 +3055,14 @@
* object from the parcel at the current dataPosition(), using the given
* class loader to load any enclosed Parcelables. Returns null if
* the previously written map object was null.
+ *
+ * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this
+ * method is still preferred use the type-safer version {@link #readHashMap(ClassLoader,
+ * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
- public final HashMap readHashMap(@Nullable ClassLoader loader)
- {
+ public HashMap readHashMap(@Nullable ClassLoader loader) {
int n = readInt();
if (n < 0) {
return null;
@@ -3247,7 +3263,14 @@
* dataPosition(). Returns null if the previously written list object was
* null. The given class loader will be used to load any enclosed
* Parcelables.
+ *
+ * @deprecated Use the type-safer version {@link #readArrayList(ClassLoader, Class)} starting
+ * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+ * use {@link #createTypedArrayList(Parcelable.Creator)} if possible (eg. if the items'
+ * class is final) since this is also more performant. Note that changing to the latter
+ * also requires changing the writes.
*/
+ @Deprecated
@Nullable
public ArrayList readArrayList(@Nullable ClassLoader loader) {
return readArrayListInternal(loader, /* clazz */ null);
@@ -3274,7 +3297,14 @@
* dataPosition(). Returns null if the previously written array was
* null. The given class loader will be used to load any enclosed
* Parcelables.
+ *
+ * @deprecated Use the type-safer version {@link #readArray(ClassLoader, Class)} starting from
+ * Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to use
+ * {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the items' class is
+ * final) since this is also more performant. Note that changing to the latter also
+ * requires changing the writes.
*/
+ @Deprecated
@Nullable
public Object[] readArray(@Nullable ClassLoader loader) {
return readArrayInternal(loader, /* clazz */ null);
@@ -3300,7 +3330,14 @@
* dataPosition(). Returns null if the previously written list object was
* null. The given class loader will be used to load any enclosed
* Parcelables.
+ *
+ * @deprecated Use the type-safer version {@link #readSparseArray(ClassLoader, Class)} starting
+ * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+ * use {@link #createTypedSparseArray(Parcelable.Creator)} if possible (eg. if the items'
+ * class is final) since this is also more performant. Note that changing to the latter
+ * also requires changing the writes.
*/
+ @Deprecated
@Nullable
public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
return readSparseArrayInternal(loader, /* clazz */ null);
@@ -4107,7 +4144,13 @@
* object has been written.
* @throws BadParcelableException Throws BadParcelableException if there
* was an error trying to instantiate the Parcelable.
+ *
+ * @deprecated Use the type-safer version {@link #readParcelable(ClassLoader, Class)} starting
+ * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+ * use {@link Parcelable.Creator#createFromParcel(Parcel)} if possible since this is also
+ * more performant. Note that changing to the latter also requires changing the writes.
*/
+ @Deprecated
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
return readParcelableInternal(loader, /* clazz */ null);
@@ -4176,7 +4219,11 @@
* read the {@link Parcelable.Creator}.
*
* @see #writeParcelableCreator
+ *
+ * @deprecated Use the type-safer version {@link #readParcelableCreator(ClassLoader, Class)}
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
return readParcelableCreatorInternal(loader, /* clazz */ null);
@@ -4337,7 +4384,11 @@
* Read and return a new Serializable object from the parcel.
* @return the Serializable object, or null if the Serializable name
* wasn't found in the parcel.
+ *
+ * @deprecated Use the type-safer version {@link #readSerializable(ClassLoader, Class)} starting
+ * from Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
+ @Deprecated
@Nullable
public Serializable readSerializable() {
return readSerializableInternal(/* loader */ null, /* clazz */ null);
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 4e8418b..ba5ed43 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -298,6 +298,17 @@
}
/**
+ * Register callback for service registration notifications.
+ *
+ * @throws RemoteException for underlying error.
+ * @hide
+ */
+ public static void registerForNotifications(
+ @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException {
+ getIServiceManager().registerForNotifications(name, callback);
+ }
+
+ /**
* Return a list of all currently running services.
* @return an array of all currently running services, or <code>null</code> in
* case of an exception
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 3739040..2dcf674 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -78,7 +78,7 @@
public void registerForNotifications(String name, IServiceCallback cb)
throws RemoteException {
- throw new RemoteException();
+ mServiceManager.registerForNotifications(name, cb);
}
public void unregisterForNotifications(String name, IServiceCallback cb)
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 447102b..94375c0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3149,6 +3149,39 @@
}
/**
+ * Creates a user with the specified {@link NewUserRequest}.
+ *
+ * @param newUserRequest specify the user information
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) {
+ UserInfo user = null;
+ int operationResult = USER_OPERATION_ERROR_UNKNOWN;
+ try {
+ user = createUser(newUserRequest.getName(), newUserRequest.getUserType(),
+ determineFlagsForUserCreation(newUserRequest));
+ } catch (UserOperationException e) {
+ Log.w(TAG, "Exception while creating user " + newUserRequest, e);
+ operationResult = e.getUserOperationResult();
+ }
+ if (user == null) {
+ return new NewUserResponse(null, operationResult);
+ }
+ return new NewUserResponse(user.getUserHandle(), USER_OPERATION_SUCCESS);
+ }
+
+ private int determineFlagsForUserCreation(NewUserRequest newUserRequest) {
+ int flags = 0;
+ if (newUserRequest.isAdmin()) flags |= UserInfo.FLAG_ADMIN;
+ if (newUserRequest.isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL;
+ return flags;
+ }
+
+ /**
* Pre-creates a user of the specified type. Default user restrictions will be applied.
*
* <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index fbac954..bff5c62 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -90,9 +90,9 @@
*/
int changeEncryptionPassword(int type, in String password) = 28;
/**
- * Returns list of all mountable volumes.
+ * Returns list of all mountable volumes for the specified userId
*/
- StorageVolume[] getVolumeList(int uid, in String packageName, int flags) = 29;
+ StorageVolume[] getVolumeList(int userId, in String callingPackage, int flags) = 29;
/**
* Determines the encryption state of the volume.
* @return a numerical value. See {@code ENCRYPTION_STATE_*} for possible
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 77c794c..627e09e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1391,13 +1391,7 @@
}
packageName = packageNames[0];
}
- final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
- if (uid <= 0) {
- Log.w(TAG, "Missing UID; no storage volumes available");
- return new StorageVolume[0];
- }
- return storageManager.getVolumeList(uid, packageName, flags);
+ return storageManager.getVolumeList(userId, packageName, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index dc01990..91ef8a5 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -20,6 +20,7 @@
import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
import android.os.Build;
+import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
@@ -71,6 +72,11 @@
private long mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+ /**
+ * The event time of the {@link AccessibilityEvent} which presents the populated windows cache
+ * before it is stale.
+ */
+ private long mValidWindowCacheTimeStamp = 0;
private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
private int mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -97,13 +103,20 @@
* The key of SparseArray is display ID.
*
* @param windowsOnAllDisplays The accessibility windows of all displays.
+ * @param populationTimeStamp The timestamp from {@link SystemClock#uptimeMillis()} when the
+ * client requests the data.
*/
public void setWindowsOnAllDisplays(
- SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays) {
+ SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays,
+ long populationTimeStamp) {
synchronized (mLock) {
if (DEBUG) {
Log.i(LOG_TAG, "Set windows");
}
+ if (mValidWindowCacheTimeStamp > populationTimeStamp) {
+ // Discard the windows because it might be stale.
+ return;
+ }
clearWindowCacheLocked();
if (windowsOnAllDisplays == null) {
return;
@@ -224,6 +237,7 @@
} break;
case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
+ mValidWindowCacheTimeStamp = event.getEventTime();
if (event.getWindowChanges()
== AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
// Don't need to clear all cache. Unless the changes are related to
@@ -232,6 +246,7 @@
break;
}
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
+ mValidWindowCacheTimeStamp = event.getEventTime();
clear();
} break;
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 6975bb2..bc21488 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -435,8 +435,10 @@
}
}
+ long populationTimeStamp;
final long identityToken = Binder.clearCallingIdentity();
try {
+ populationTimeStamp = SystemClock.uptimeMillis();
windows = connection.getWindows();
} finally {
Binder.restoreCallingIdentity(identityToken);
@@ -446,7 +448,7 @@
}
if (windows != null) {
if (sAccessibilityCache != null) {
- sAccessibilityCache.setWindowsOnAllDisplays(windows);
+ sAccessibilityCache.setWindowsOnAllDisplays(windows, populationTimeStamp);
}
return windows;
}
diff --git a/core/res/res/layout-watch/alert_dialog_material.xml b/core/res/res/layout-watch/alert_dialog_material.xml
index 960b927..1d56845 100644
--- a/core/res/res/layout-watch/alert_dialog_material.xml
+++ b/core/res/res/layout-watch/alert_dialog_material.xml
@@ -51,7 +51,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|top"
- android:textAppearance="@style/TextAppearance.Material.Subhead"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
android:paddingStart="?dialogPreferredPadding"
android:paddingEnd="?dialogPreferredPadding"
android:paddingTop="8dip"
diff --git a/core/res/res/layout-watch/progress_dialog_material.xml b/core/res/res/layout-watch/progress_dialog_material.xml
index 96bda1d..a7df948 100644
--- a/core/res/res/layout-watch/progress_dialog_material.xml
+++ b/core/res/res/layout-watch/progress_dialog_material.xml
@@ -40,7 +40,7 @@
<TextView
android:id="@+id/message"
- android:textAppearance="?attr/textAppearanceListItem"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Subhead"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
diff --git a/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml b/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
index 69c241c..68ccb3d 100644
--- a/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
+++ b/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="24dp"
- android:insetRight="24dp"
- android:insetTop="24dp"
- android:insetBottom="24dp">
+ android:insetLeft="22.22%"
+ android:insetRight="22.22%"
+ android:insetTop="22.22%"
+ android:insetBottom="22.22%">
<vector
android:width="60dp"
android:height="60dp"
diff --git a/core/res/res/values-w198dp/dimens_material.xml b/core/res/res/values-w198dp/dimens_material.xml
new file mode 100644
index 0000000..a8aed25
--- /dev/null
+++ b/core/res/res/values-w198dp/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="screen_percentage_05">9.9dp</dimen>
+ <dimen name="screen_percentage_10">19.8dp</dimen>
+ <dimen name="screen_percentage_15">29.7dp</dimen>
+</resources>
diff --git a/core/res/res/values-w208dp/dimens_material.xml b/core/res/res/values-w208dp/dimens_material.xml
new file mode 100644
index 0000000..069eeb0
--- /dev/null
+++ b/core/res/res/values-w208dp/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="screen_percentage_05">10.4dp</dimen>
+ <dimen name="screen_percentage_10">20.8dp</dimen>
+ <dimen name="screen_percentage_15">31.2dp</dimen>
+</resources>
diff --git a/core/res/res/values-w211dp/dimens_material.xml b/core/res/res/values-w211dp/dimens_material.xml
new file mode 100644
index 0000000..bd7ca9a
--- /dev/null
+++ b/core/res/res/values-w211dp/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="screen_percentage_05">10.55dp</dimen>
+ <dimen name="screen_percentage_10">21.1dp</dimen>
+ <dimen name="screen_percentage_15">31.65dp</dimen>
+</resources>
diff --git a/core/res/res/values-w227dp/dimens_material.xml b/core/res/res/values-w227dp/dimens_material.xml
new file mode 100644
index 0000000..eb4df8a2
--- /dev/null
+++ b/core/res/res/values-w227dp/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="screen_percentage_05">11.35dp</dimen>
+ <dimen name="screen_percentage_10">22.7dp</dimen>
+ <dimen name="screen_percentage_15">34.05dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index cd809b8..cf0488b 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -27,10 +27,6 @@
<item>restart</item>
</string-array>
- <!-- Base "touch slop" value used by ViewConfiguration as a
- movement threshold where scrolling should begin. -->
- <dimen name="config_viewConfigurationTouchSlop">4dp</dimen>
-
<!-- Minimum velocity to initiate a fling, as measured in dips per second. -->
<dimen name="config_viewMinFlingVelocity">500dp</dimen>
diff --git a/core/res/res/values-watch/donottranslate.xml b/core/res/res/values-watch/donottranslate.xml
index d247ff6..f328def 100644
--- a/core/res/res/values-watch/donottranslate.xml
+++ b/core/res/res/values-watch/donottranslate.xml
@@ -17,6 +17,6 @@
<resources>
<!-- DO NOT TRANSLATE Spans within this text are applied to style composing regions
within an EditText widget. The text content is ignored and not used.
- Note: This is @color/material_deep_teal_200, cannot use @color references here. -->
- <string name="candidates_style" translatable="false"><font color="#80cbc4">candidates</font></string>
+ Note: This is @color/GM2_blue_300, cannot use @color references here. -->
+ <string name="candidates_style" translatable="false"><font color="#8AB4F8">candidates</font></string>
</resources>
diff --git a/core/res/res/values-watch/styles_device_default.xml b/core/res/res/values-watch/styles_device_default.xml
new file mode 100644
index 0000000..e2261af
--- /dev/null
+++ b/core/res/res/values-watch/styles_device_default.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
+ <item name="maxLines">2</item>
+ <item name="shadowRadius">0</item>
+ <item name="ellipsize">end</item>
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Title</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
+ <item name="android:textSize">15sp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:textStyle">bold</item>
+ </style>
+ <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
+ <item name="android:textSize">16sp</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ </style>
+</resources>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 1f27063..5db6a3e 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -309,18 +309,22 @@
final int numOfConfig = activity.mNumOfConfigChanges;
final Configuration processConfigLandscape = new Configuration();
+ processConfigLandscape.orientation = ORIENTATION_LANDSCAPE;
processConfigLandscape.windowConfiguration.setBounds(new Rect(0, 0, 100, 60));
processConfigLandscape.seq = BASE_SEQ + 1;
final Configuration activityConfigLandscape = new Configuration();
+ activityConfigLandscape.orientation = ORIENTATION_LANDSCAPE;
activityConfigLandscape.windowConfiguration.setBounds(new Rect(0, 0, 100, 50));
activityConfigLandscape.seq = BASE_SEQ + 2;
final Configuration processConfigPortrait = new Configuration();
+ processConfigPortrait.orientation = ORIENTATION_PORTRAIT;
processConfigPortrait.windowConfiguration.setBounds(new Rect(0, 0, 60, 100));
processConfigPortrait.seq = BASE_SEQ + 3;
final Configuration activityConfigPortrait = new Configuration();
+ activityConfigPortrait.orientation = ORIENTATION_PORTRAIT;
activityConfigPortrait.windowConfiguration.setBounds(new Rect(0, 0, 50, 100));
activityConfigPortrait.seq = BASE_SEQ + 4;
@@ -348,7 +352,8 @@
assertEquals(activityConfigPortrait.windowConfiguration.getBounds(), bounds);
// Ensure that Activity#onConfigurationChanged() not be called because the changes in
- // WindowConfiguration shouldn't be reported.
+ // WindowConfiguration shouldn't be reported, and we only apply the latest Configuration
+ // update in transaction.
assertEquals(numOfConfig, activity.mNumOfConfigChanges);
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 0e78f87..33c6a4b 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.SystemClock;
import android.util.SparseArray;
import android.view.Display;
import android.view.View;
@@ -299,7 +300,8 @@
SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
allWindows.put(Display.DEFAULT_DISPLAY, windowsIn1);
allWindows.put(SECONDARY_DISPLAY_ID, windowsIn2);
- mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ final long populationTimeStamp = SystemClock.uptimeMillis();
+ mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp);
// Gets windows at default display.
windowsOut1 = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
@@ -339,6 +341,46 @@
}
@Test
+ public void setInvalidWindowsAfterWindowsChangedEvent_notInCache() {
+ final AccessibilityEvent event = new AccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOWS_CHANGED);
+ final long eventTime = 1000L;
+ event.setEventTime(eventTime);
+ mAccessibilityCache.onAccessibilityEvent(event);
+
+ final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1,
+ SPECIFIC_WINDOW_LAYER);
+ List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10);
+
+ try {
+ assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY));
+ } finally {
+ windowInfo1.recycle();
+ }
+ }
+
+ @Test
+ public void setInvalidWindowsAfterStateChangedEvent_notInCache() {
+ final AccessibilityEvent event = new AccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ final long eventTime = 1000L;
+ event.setEventTime(eventTime);
+ mAccessibilityCache.onAccessibilityEvent(event);
+
+ final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1,
+ SPECIFIC_WINDOW_LAYER);
+ List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10);
+
+ try {
+ assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY));
+ } finally {
+ windowInfo1.recycle();
+ }
+ }
+
+ @Test
public void addWindowThenStateChangedEvent_noLongerInCache() {
putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
DEFAULT_WINDOW_LAYER);
@@ -1063,9 +1105,14 @@
}
private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows) {
+ setWindowsByDisplay(displayId, windows, SystemClock.uptimeMillis());
+ }
+
+ private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows,
+ long populationTimeStamp) {
SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
allWindows.put(displayId, windows);
- mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp);
}
private List<AccessibilityWindowInfo> getWindowsByDisplay(int displayId) {
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 055fc71..db63e6e 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -160,6 +160,34 @@
verify(callback).onStateChanged(eq(mService.getBaseState()));
}
+ @Test
+ public void verifyDeviceStateRequestCallbacksCalled() {
+ DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request,
+ ConcurrentUtils.DIRECT_EXECUTOR /* executor */,
+ callback /* callback */);
+
+ verify(callback).onRequestActivated(eq(request));
+ Mockito.reset(callback);
+
+ mDeviceStateManagerGlobal.cancelRequest(request);
+
+ verify(callback).onRequestCanceled(eq(request));
+ }
+
+ public static class TestDeviceStateRequestCallback implements DeviceStateRequest.Callback {
+ @Override
+ public void onRequestActivated(DeviceStateRequest request) { }
+
+ @Override
+ public void onRequestCanceled(DeviceStateRequest request) { }
+
+ @Override
+ public void onRequestSuspended(DeviceStateRequest request) { }
+ }
+
private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
public static final class Request {
public final IBinder token;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index be1e2b2..88228f2 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-
// Sysconfig files
package {
@@ -119,6 +118,13 @@
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.intentresolver",
+ sub_dir: "permissions",
+ src: "com.android.intentresolver.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.launcher3",
system_ext_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.intentresolver.xml b/data/etc/com.android.intentresolver.xml
new file mode 100644
index 0000000..0f1c467
--- /dev/null
+++ b/data/etc/com.android.intentresolver.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.intentresolver">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index 194b633..89d7a40 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -39,6 +39,8 @@
final Transformation mTransformation = new Transformation();
final float[] mMatrix = new float[9];
+ final float[] mVecs = new float[4];
+ final Rect mRect = new Rect();
private boolean mIsFirstFrame = true;
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@@ -76,6 +78,22 @@
mTarget.localBounds.left, mTarget.localBounds.top);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
+
+ // Open/close animation may scale up the surface. Apply an inverse scale to the window crop
+ // so that it will not be covering other windows.
+ mVecs[1] = mVecs[2] = 0;
+ mVecs[0] = mVecs[3] = 1;
+ mTransformation.getMatrix().mapVectors(mVecs);
+ mVecs[0] = 1.f / mVecs[0];
+ mVecs[3] = 1.f / mVecs[3];
+ final Rect clipRect = mTarget.localBounds;
+ mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+ mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+ mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+ mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+ mRect.offsetTo(Math.round(mTarget.localBounds.width() * (1 - mVecs[0]) / 2.f),
+ Math.round(mTarget.localBounds.height() * (1 - mVecs[3]) / 2.f));
+ t.setWindowCrop(mLeash, mRect);
}
/** Called after animation finished. */
@@ -157,8 +175,6 @@
* Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
*/
static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
- private final float[] mVecs = new float[4];
- private final Rect mRect = new Rect();
BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
super(animation, target);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 32d447e..fe9ce97 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -18,9 +18,12 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.window.common.DisplayFeature.COMMON_STATE_FLAT;
+import static androidx.window.common.DisplayFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+import android.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
@@ -119,22 +122,45 @@
return !mWindowLayoutChangeListeners.isEmpty();
}
- private int getFeatureState(DisplayFeature feature) {
+ /**
+ * Calculate the {@link DisplayFeature.State} from the feature or the device posture producer.
+ * If the given {@link DisplayFeature.State} is not valid then {@code null} will be returned.
+ * The {@link FoldingFeature} should be ignored in the case of an invalid
+ * {@link DisplayFeature.State}.
+ *
+ * @param feature a {@link DisplayFeature} to provide the feature state if present.
+ * @return {@link DisplayFeature.State} of the hinge if present or the state from the posture
+ * produce if present.
+ */
+ @Nullable
+ private Integer getFeatureState(DisplayFeature feature) {
Integer featureState = feature.getState();
Optional<Integer> posture = mDevicePostureProducer.getData();
- int fallbackPosture = posture.orElse(DisplayFeature.COMMON_STATE_FLAT);
- int displayFeatureState = featureState == null ? fallbackPosture : featureState;
- return convertToExtensionState(displayFeatureState);
+ Integer state = featureState == null ? posture.orElse(null) : featureState;
+ return convertToExtensionState(state);
}
- private int convertToExtensionState(int state) {
- switch (state) {
- case DisplayFeature.COMMON_STATE_FLAT:
- return FoldingFeature.STATE_FLAT;
- case DisplayFeature.COMMON_STATE_HALF_OPENED:
- return FoldingFeature.STATE_HALF_OPENED;
+ /**
+ * A convenience method to translate from the common feature state to the extensions feature
+ * state. More specifically, translates from {@link DisplayFeature.State} to
+ * {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not
+ * possible to translate, then we will return a {@code null} value.
+ *
+ * @param state if it matches a value in {@link DisplayFeature.State}, {@code null} otherwise.
+ * @return a {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED} if
+ * the given state matches a value in {@link DisplayFeature.State} and {@code null} otherwise.
+ */
+ @Nullable
+ private Integer convertToExtensionState(@Nullable Integer state) {
+ if (state == null) { // The null check avoids a NullPointerException.
+ return null;
+ } else if (state == COMMON_STATE_FLAT) {
+ return FoldingFeature.STATE_FLAT;
+ } else if (state == COMMON_STATE_HALF_OPENED) {
+ return FoldingFeature.STATE_HALF_OPENED;
+ } else {
+ return null;
}
- return FoldingFeature.STATE_FLAT;
}
private void onDisplayFeaturesChanged() {
@@ -151,6 +177,25 @@
return new WindowLayoutInfo(displayFeatures);
}
+ /**
+ * Translate from the {@link DisplayFeature} to
+ * {@link androidx.window.extensions.layout.DisplayFeature} for a given {@link Activity}. If a
+ * {@link DisplayFeature} is not valid then it will be omitted.
+ *
+ * For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window
+ * coordinate space and the state is calculated either from {@link DisplayFeature#getState()} or
+ * {@link #mDisplayFeatureProducer}. The state from {@link #mDisplayFeatureProducer} may not be
+ * valid since {@link #mDisplayFeatureProducer} is a general state controller. If the state is
+ * not valid, the {@link FoldingFeature} is omitted from the {@link List} of
+ * {@link androidx.window.extensions.layout.DisplayFeature}. If the bounds are not valid,
+ * constructing a {@link FoldingFeature} will throw an {@link IllegalArgumentException} since
+ * this can cause negative UI effects down stream.
+ *
+ * @param activity a proxy for the {@link android.view.Window} that contains the
+ * {@link androidx.window.extensions.layout.DisplayFeature}.
+ * @return a {@link List} of valid {@link androidx.window.extensions.layout.DisplayFeature} that
+ * are within the {@link android.view.Window} of the {@link Activity}
+ */
private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
@NonNull Activity activity) {
List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
@@ -170,6 +215,10 @@
if (storedFeatures.isPresent()) {
for (DisplayFeature baseFeature : storedFeatures.get()) {
+ Integer state = getFeatureState(baseFeature);
+ if (state == null) {
+ continue;
+ }
Rect featureRect = baseFeature.getRect();
rotateRectToDisplayRotation(displayId, featureRect);
transformToWindowSpaceRect(activity, featureRect);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index f567877..c3ce362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -74,7 +74,7 @@
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
- Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
+ Optional<FreeformTaskListener> freeformTaskListenerOptional,
Optional<RecentTasksController> recentTasks,
Transitions transitions,
StartingWindowController startingWindow,
@@ -90,7 +90,7 @@
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
- mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
+ mFreeformTaskListenerOptional = freeformTaskListenerOptional;
mRecentTasks = recentTasks;
mTransitions = transitions;
mMainExecutor = mainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java
new file mode 100644
index 0000000..806f795
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * This is a qualifier that Shell uses to workaround an issue with providing nullable optionals
+ * which are by default unbound.
+ *
+ * For example, ideally we would have this scenario:
+ * BaseModule:
+ * @BindsOptionalOf
+ * abstract Optional<Interface> optionalInterface();
+ *
+ * SpecializedModule:
+ * @Provides
+ * static Interface providesInterface() {
+ * return new InterfaceImpl();
+ * }
+ *
+ * However, if the interface is supposed to be provided dynamically, then Dagger is not able to bind
+ * the optional interface to a null instance, and @BindsOptionalOf does not support @Nullable
+ * instances of the interface provided by the specialized module.
+ *
+ * For example, this does not work:
+ * BaseModule:
+ * @BindsOptionalOf
+ * abstract Optional<Interface> optionalInterface();
+ *
+ * SpecializedModule:
+ * @Provides
+ * static Interface providesInterface() {
+ * if (systemSupportsInterfaceFeature) {
+ * return new InterfaceImpl();
+ * } else {
+ * return null;
+ * }
+ * }
+ *
+ * To workaround this, we can instead upstream the check (assuming it can be upstreamed into the
+ * base module), and then always provide a non-null instance in the specialized module.
+ *
+ * For example:
+ * BaseModule:
+ * @BindsOptionalOf
+ * @DynamicOverride
+ * abstract Interface dynamicInterface();
+ *
+ * @Provides
+ * static Optional<Interface> providesOptionalInterface(
+ * @DynamicOverride Optional<Interface> interface) {
+ * if (systemSupportsInterfaceFeature) {
+ * return interface;
+ * }
+ * return Optional.empty();
+ * }
+ *
+ * SpecializedModule:
+ * @Provides
+ * @DynamicOverride
+ * static Interface providesInterface() {
+ * return new InterfaceImpl();
+ * }
+ *
+ * This is also useful in cases where there needs to be a default implementation in the base module
+ * which is also overridable in the specialized module. This isn't generally recommended, but
+ * due to the nature of Shell modules being referenced from a number of various projects, this
+ * can be useful for *required* components that
+ * 1) clearly identifies which are intended for overriding in the base module, and
+ * 2) allows us to declare a default implementation in the base module, without having to force
+ * every SysUI impl to explicitly provide it (if a large number of them share the default impl)
+ *
+ * For example, this uses the same setup as above, but the interface provided (if bound) is used
+ * otherwise the default is created:
+ * @BindsOptionalOf
+ * @DynamicOverride
+ * abstract Interface dynamicInterface();
+ *
+ * @Provides
+ * static Optional<Interface> providesOptionalInterface(
+ * @DynamicOverride Optional<Interface> overrideInterfaceImpl) {
+ * if (overrideInterfaceImpl.isPresent()) {
+ * return overrideInterfaceImpl.get();
+ * }
+ * return new DefaultImpl();
+ * }
+ *
+ * SpecializedModule:
+ * @Provides
+ * @DynamicOverride
+ * static Interface providesInterface() {
+ * return new SuperSpecialImpl();
+ * }
+ */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DynamicOverride {}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 6997d60..15bfeb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -16,25 +16,16 @@
package com.android.wm.shell.dagger;
-import android.animation.AnimationHandler;
-import android.content.Context;
import android.view.IWindowManager;
-import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm;
-import com.android.wm.shell.transition.Transitions;
import dagger.Module;
import dagger.Provides;
@@ -51,42 +42,12 @@
public class TvWMShellModule {
//
- // Internal common - Components used internally by multiple shell features
- //
-
- @WMSingleton
- @Provides
- static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, DisplayInsetsController displayInsetsController,
- @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, displayInsetsController,
- mainExecutor, transactionPool);
- }
-
- //
- // Split/multiwindow
- //
-
- @WMSingleton
- @Provides
- static LegacySplitScreenController provideSplitScreen(Context context,
- DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController displayImeController, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
- TaskStackListenerImpl taskStackListener, Transitions transitions,
- @ShellMainThread ShellExecutor mainExecutor,
- @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
- return new LegacySplitScreenController(context, displayController, systemWindows,
- displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
- taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
- }
-
- //
// Starting Windows (Splash Screen)
//
@WMSingleton
@Provides
+ @DynamicOverride
static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
return new TvStartingWindowTypeAlgorithm();
};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index ac2e448..3f5b70b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -16,16 +16,16 @@
package com.android.wm.shell.dagger;
+import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;
+
import android.app.ActivityTaskManager;
import android.content.Context;
-import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.SystemProperties;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -77,23 +77,19 @@
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
+import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
-import com.android.wm.shell.unfold.UnfoldBackgroundController;
import java.util.Optional;
-import javax.inject.Provider;
-
import dagger.BindsOptionalOf;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -128,6 +124,28 @@
return new DisplayInsetsController(wmService, displayController, mainExecutor);
}
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract DisplayImeController optionalDisplayImeController();
+
+ @WMSingleton
+ @Provides
+ static DisplayImeController provideDisplayImeController(
+ @DynamicOverride Optional<DisplayImeController> overrideDisplayImeController,
+ IWindowManager wmService,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor,
+ TransactionPool transactionPool
+ ) {
+ if (overrideDisplayImeController.isPresent()) {
+ return overrideDisplayImeController.get();
+ }
+ return new DisplayImeController(wmService, displayController, displayInsetsController,
+ mainExecutor, transactionPool);
+ }
+
@WMSingleton
@Provides
static DisplayLayout provideDisplayLayout() {
@@ -202,7 +220,7 @@
}
//
- // Bubbles
+ // Bubbles (optional feature)
//
@WMSingleton
@@ -211,27 +229,8 @@
return bubbleController.map((controller) -> controller.asBubbles());
}
- // Note: Handler needed for LauncherApps.register
- @WMSingleton
- @Provides
- static Optional<BubbleController> provideBubbleController(Context context,
- FloatingContentCoordinator floatingContentCoordinator,
- IStatusBarService statusBarService,
- WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps,
- TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- ShellTaskOrganizer organizer,
- DisplayController displayController,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler,
- SyncTransactionQueue syncQueue) {
- return Optional.of(BubbleController.create(context, null /* synchronizer */,
- floatingContentCoordinator, statusBarService, windowManager,
- windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
- }
+ @BindsOptionalOf
+ abstract BubbleController optionalBubblesController();
//
// Fullscreen
@@ -252,59 +251,45 @@
// Unfold transition
//
+ @BindsOptionalOf
+ abstract ShellUnfoldProgressProvider optionalShellUnfoldProgressProvider();
+
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract FullscreenUnfoldController optionalFullscreenUnfoldController();
+
@WMSingleton
@Provides
static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
- Context context,
- Optional<ShellUnfoldProgressProvider> progressProvider,
- Lazy<UnfoldBackgroundController> unfoldBackgroundController,
- DisplayInsetsController displayInsetsController,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
- return progressProvider.map(shellUnfoldTransitionProgressProvider ->
- new FullscreenUnfoldController(context, mainExecutor,
- unfoldBackgroundController.get(), shellUnfoldTransitionProgressProvider,
- displayInsetsController));
- }
-
- @Provides
- static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
- Optional<ShellUnfoldProgressProvider> progressProvider,
- Context context,
- TransactionPool transactionPool,
- Lazy<UnfoldBackgroundController> unfoldBackgroundController,
- DisplayInsetsController displayInsetsController,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
- return progressProvider.map(shellUnfoldTransitionProgressProvider ->
- new StageTaskUnfoldController(
- context,
- transactionPool,
- shellUnfoldTransitionProgressProvider,
- displayInsetsController,
- unfoldBackgroundController.get(),
- mainExecutor
- ));
- }
-
- @WMSingleton
- @Provides
- static UnfoldBackgroundController provideUnfoldBackgroundController(
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- Context context
- ) {
- return new UnfoldBackgroundController(
- context,
- rootTaskDisplayAreaOrganizer
- );
+ @DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController,
+ Optional<ShellUnfoldProgressProvider> progressProvider) {
+ if (progressProvider.isPresent()
+ && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
+ return fullscreenUnfoldController;
+ }
+ return Optional.empty();
}
//
// Freeform (optional feature)
//
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf
- abstract Optional<FreeformTaskListener> optionalFreeformTaskListener();
+ @DynamicOverride
+ abstract FreeformTaskListener optionalFreeformTaskListener();
+
+ @WMSingleton
+ @Provides
+ static Optional<FreeformTaskListener> provideFreeformTaskListener(
+ @DynamicOverride Optional<FreeformTaskListener> freeformTaskListener,
+ Context context) {
+ if (FreeformTaskListener.isFreeformEnabled(context)) {
+ return freeformTaskListener;
+ }
+ return Optional.empty();
+ }
//
// Hide display cutout
@@ -335,20 +320,22 @@
return oneHandedController.map((controller) -> controller.asOneHanded());
}
- // Needs the shell main handler for ContentObserver callbacks
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract OneHandedController optionalOneHandedController();
+
@WMSingleton
@Provides
- static Optional<OneHandedController> provideOneHandedController(Context context,
- WindowManager windowManager, DisplayController displayController,
- DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return Optional.ofNullable(OneHandedController.create(context, windowManager,
- displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
- mainHandler));
+ static Optional<OneHandedController> providesOneHandedController(
+ @DynamicOverride Optional<OneHandedController> oneHandedController) {
+ if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ return oneHandedController;
+ }
+ return Optional.empty();
}
+
//
// Task to Surface communication
//
@@ -366,15 +353,6 @@
return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor));
}
- @WMSingleton
- @Provides
- static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
- @ShellMainThread ShellExecutor mainExecutor,
- RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
- return Optional.ofNullable(new DisplayAreaHelperController(mainExecutor,
- rootDisplayAreaOrganizer));
- }
-
//
// Pip (optional feature)
//
@@ -460,7 +438,7 @@
}
//
- // Split/multiwindow
+ // Display areas
//
@WMSingleton
@@ -479,31 +457,38 @@
@WMSingleton
@Provides
+ static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
+ @ShellMainThread ShellExecutor mainExecutor,
+ RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
+ return Optional.of(new DisplayAreaHelperController(mainExecutor,
+ rootDisplayAreaOrganizer));
+ }
+
+ //
+ // Splitscreen (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
static Optional<SplitScreen> provideSplitScreen(
Optional<SplitScreenController> splitScreenController) {
return splitScreenController.map((controller) -> controller.asSplitScreen());
}
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract SplitScreenController optionalSplitScreenController();
+
@WMSingleton
@Provides
- static Optional<SplitScreenController> provideSplitScreenController(
- ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- @ShellMainThread ShellExecutor mainExecutor,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
+ static Optional<SplitScreenController> providesSplitScreenController(
+ @DynamicOverride Optional<SplitScreenController> splitscreenController,
+ Context context) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
- return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
- displayInsetsController, transitions, transactionPool, iconProvider,
- recentTasks, stageTaskUnfoldControllerProvider));
- } else {
- return Optional.empty();
+ return splitscreenController;
}
+ return Optional.empty();
}
// Legacy split (optional feature)
@@ -529,7 +514,9 @@
@BindsOptionalOf
abstract AppPairsController optionalAppPairs();
+ //
// Starting window
+ //
@WMSingleton
@Provides
@@ -548,6 +535,23 @@
startingWindowTypeAlgorithm, iconProvider, pool);
}
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract StartingWindowTypeAlgorithm optionalStartingWindowTypeAlgorithm();
+
+ @WMSingleton
+ @Provides
+ static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm(
+ @DynamicOverride Optional<StartingWindowTypeAlgorithm> startingWindowTypeAlgorithm
+ ) {
+ if (startingWindowTypeAlgorithm.isPresent()) {
+ return startingWindowTypeAlgorithm.get();
+ }
+ // Default to phone starting window type
+ return new PhoneStartingWindowTypeAlgorithm();
+ }
+
//
// Task view factory
//
@@ -591,7 +595,7 @@
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
- Optional<Optional<FreeformTaskListener>> freeformTaskListener,
+ Optional<FreeformTaskListener> freeformTaskListener,
Optional<RecentTasksController> recentTasksOptional,
Transitions transitions,
StartingWindowController startingWindow,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index ec70147..46c7b50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -18,15 +18,22 @@
import android.animation.AnimationHandler;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.os.Handler;
-import android.view.IWindowManager;
+import android.view.WindowManager;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -36,6 +43,7 @@
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.freeform.FreeformTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -55,13 +63,18 @@
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
-import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
+import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
import java.util.Optional;
+import javax.inject.Provider;
+
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -77,17 +90,29 @@
public class WMShellModule {
//
- // Internal common - Components used internally by multiple shell features
+ // Bubbles
//
+ // Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
- static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, DisplayInsetsController displayInsetsController,
+ static BubbleController provideBubbleController(Context context,
+ FloatingContentCoordinator floatingContentCoordinator,
+ IStatusBarService statusBarService,
+ WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ LauncherApps launcherApps,
+ TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
+ ShellTaskOrganizer organizer,
+ DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, displayInsetsController,
- mainExecutor, transactionPool);
+ @ShellMainThread Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
+ return BubbleController.create(context, null /* synchronizer */,
+ floatingContentCoordinator, statusBarService, windowManager,
+ windowManagerShellWrapper, launcherApps, taskStackListener,
+ uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue);
}
//
@@ -96,16 +121,55 @@
@WMSingleton
@Provides
- static Optional<FreeformTaskListener> provideFreeformTaskListener(
- Context context,
+ @DynamicOverride
+ static FreeformTaskListener provideFreeformTaskListener(
SyncTransactionQueue syncQueue) {
- return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue));
+ return new FreeformTaskListener(syncQueue);
}
//
- // Split/multiwindow
+ // One handed mode
//
+
+ // Needs the shell main handler for ContentObserver callbacks
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static OneHandedController provideOneHandedController(Context context,
+ WindowManager windowManager, DisplayController displayController,
+ DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return OneHandedController.create(context, windowManager,
+ displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
+ mainHandler);
+ }
+
+ //
+ // Splitscreen
+ //
+
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static SplitScreenController provideSplitScreenController(
+ ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue, Context context,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController, Transitions transitions,
+ TransactionPool transactionPool, IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
+ Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
+ return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
+ rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
+ displayInsetsController, transitions, transactionPool, iconProvider,
+ recentTasks, stageTaskUnfoldControllerProvider);
+ }
+
@WMSingleton
@Provides
static LegacySplitScreenController provideLegacySplitScreen(Context context,
@@ -258,12 +322,53 @@
}
//
- // Starting Windows (Splash Screen)
+ // Unfold transition
//
@WMSingleton
@Provides
- static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
- return new PhoneStartingWindowTypeAlgorithm();
+ @DynamicOverride
+ static FullscreenUnfoldController provideFullscreenUnfoldController(
+ Context context,
+ Optional<ShellUnfoldProgressProvider> progressProvider,
+ Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ return new FullscreenUnfoldController(context, mainExecutor,
+ unfoldBackgroundController.get(), progressProvider.get(),
+ displayInsetsController);
+ }
+
+ @Provides
+ static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
+ Optional<ShellUnfoldProgressProvider> progressProvider,
+ Context context,
+ TransactionPool transactionPool,
+ Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ return progressProvider.map(shellUnfoldTransitionProgressProvider ->
+ new StageTaskUnfoldController(
+ context,
+ transactionPool,
+ shellUnfoldTransitionProgressProvider,
+ displayInsetsController,
+ unfoldBackgroundController.get(),
+ mainExecutor
+ ));
+ }
+
+ @WMSingleton
+ @Provides
+ static UnfoldBackgroundController provideUnfoldBackgroundController(
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Context context
+ ) {
+ return new UnfoldBackgroundController(
+ context,
+ rootTaskDisplayAreaOrganizer
+ );
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 8a8d7c6..5c8e7d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -141,16 +141,4 @@
|| Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
}
-
- /**
- * Creates {@link FreeformTaskListener} if freeform is enabled.
- */
- public static FreeformTaskListener create(Context context,
- SyncTransactionQueue syncQueue) {
- if (!isFreeformEnabled(context)) {
- return null;
- }
-
- return new FreeformTaskListener(syncQueue);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index 80ab166..67e487d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -172,6 +172,14 @@
};
mWindowManager = new DividerWindowManager(mSystemWindows);
+
+ // No need to listen to display window container or create root tasks if the device is not
+ // using legacy split screen.
+ if (!context.getResources().getBoolean(com.android.internal.R.bool.config_useLegacySplit)) {
+ return;
+ }
+
+
mDisplayController.addDisplayWindowListener(this);
// Don't initialize the divider or anything until we get the default display.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 90074371..e068614 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -46,7 +46,6 @@
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
@@ -76,7 +75,7 @@
private static final int OVERLAY_ENABLED_DELAY_MS = 250;
private static final int DISPLAY_AREA_READY_RETRY_MS = 10;
- static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+ public static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
private volatile boolean mIsOneHandedEnabled;
private volatile boolean mIsSwipeToNotificationEnabled;
@@ -198,16 +197,10 @@
/**
* Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
*/
- @Nullable
public static OneHandedController create(
Context context, WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
- if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
- Slog.w(TAG, "Device doesn't support OneHanded feature");
- return null;
- }
-
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index f25cff7..d18bcfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -491,25 +491,12 @@
if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) {
// Calculate the snap fraction of the current stack along the old movement bounds
final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
- final float snapFraction = pipSnapAlgorithm.getSnapFraction(mPipBoundsState.getBounds(),
- mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds()),
+ final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
+ final float snapFraction = pipSnapAlgorithm.getSnapFraction(postChangeStackBounds,
+ mPipBoundsAlgorithm.getMovementBounds(postChangeStackBounds),
mPipBoundsState.getStashedState());
updateDisplayLayout.run();
- final Rect postChangeStackBounds;
- if (mPipBoundsState.getBounds() != null
- && (mPipBoundsState.getBounds().width() > mPipBoundsState.getMaxSize().x
- || mPipBoundsState.getBounds().height() > mPipBoundsState.getMaxSize().y)) {
- postChangeStackBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x,
- mPipBoundsState.getMaxSize().y);
- } else if (mPipBoundsState.getBounds() != null
- && (mPipBoundsState.getBounds().width() < mPipBoundsState.getMinSize().x
- || mPipBoundsState.getBounds().height() < mPipBoundsState.getMinSize().y)) {
- postChangeStackBounds = new Rect(0, 0, mPipBoundsState.getMinSize().x,
- mPipBoundsState.getMinSize().y);
- } else {
- postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
- }
// Calculate the stack bounds in the new orientation based on same fraction along the
// rotated movement bounds.
@@ -521,7 +508,7 @@
mPipBoundsState.getDisplayBounds(),
mPipBoundsState.getDisplayLayout().stableInsets());
- mTouchHandler.getMotionHelper().animateResizedBounds(postChangeStackBounds);
+ mTouchHandler.getMotionHelper().movePip(postChangeStackBounds);
} else {
updateDisplayLayout.run();
}
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 c634b7f..96fd59f 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
@@ -69,7 +69,6 @@
private static final int UNSTASH_DURATION = 250;
private static final int LEAVE_PIP_DURATION = 300;
private static final int SHIFT_DURATION = 300;
- private static final int ANIMATE_PIP_RESIZE_ANIMATION = 250;
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 1.9f;
@@ -549,14 +548,6 @@
}
/**
- * Animates the PiP from an old bound to a new bound. This is mostly used when display
- * has changed and PiP bounds needs to be changed.
- */
- void animateResizedBounds(Rect newBounds) {
- resizeAndAnimatePipUnchecked(newBounds, ANIMATE_PIP_RESIZE_ANIMATION);
- }
-
- /**
* Animates the PiP to offset it from the IME or shelf.
*/
void animateToOffset(Rect originalBounds, int offset) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 7457be2..8af72a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -130,19 +130,6 @@
private StageCoordinator mStageCoordinator;
- // TODO(b/205019015): Remove after we clean up downstream modules
- public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- ShellExecutor mainExecutor, DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- this(shellTaskOrganizer, syncQueue, context, rootTDAOrganizer, mainExecutor,
- displayImeController, displayInsetsController, transitions, transactionPool,
- iconProvider, Optional.empty(), unfoldControllerProvider);
- }
-
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
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 050d255..d494191 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
@@ -78,7 +78,6 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
@@ -157,10 +156,6 @@
private boolean mExitSplitScreenOnHide;
private boolean mKeyguardOccluded;
- // TODO(b/187041611): remove this flag after totally deprecated legacy split
- /** Whether the device is supporting legacy split or not. */
- private boolean mUseLegacySplit;
-
@SplitScreen.StageType
private int mDismissTop = NO_DISMISS;
@@ -735,17 +730,9 @@
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
- mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
-
- // Only sets side stage as launch-adjacent-flag-root when the device is not using legacy
- // split to prevent new split behavior confusing users.
- if (!mUseLegacySplit) {
- wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- }
-
mTaskOrganizer.applyTransaction(wct);
}
}
@@ -755,11 +742,6 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Deactivate the main stage if it no longer has a root task.
mMainStage.deactivate(wct);
-
- if (!mUseLegacySplit) {
- wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- }
-
mTaskOrganizer.applyTransaction(wct);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 190006e..62b8638 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -37,6 +37,7 @@
import androidx.annotation.NonNull;
+import com.android.internal.R;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SurfaceUtils;
@@ -102,7 +103,12 @@
mSurfaceSession = surfaceSession;
mIconProvider = iconProvider;
mStageTaskUnfoldController = stageTaskUnfoldController;
- taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
+
+ // No need to create root task if the device is using legacy split screen.
+ // TODO(b/199236198): Remove this check after totally deprecated legacy split.
+ if (!context.getResources().getBoolean(R.bool.config_useLegacySplit)) {
+ taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
+ }
}
int getChildCount() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 7abda99..a0d9d03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -52,9 +52,12 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
@@ -66,6 +69,7 @@
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -78,6 +82,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.DisplayController;
@@ -292,9 +297,12 @@
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+ boolean requireBackgroundForTransition = false;
+
final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
+ final boolean isTask = change.getTaskInfo() != null;
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
int rotateDelta = change.getEndRotation() - change.getStartRotation();
@@ -342,7 +350,7 @@
startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - change.getEndRelOffset().x,
change.getEndAbsBounds().top - change.getEndRelOffset().y);
- if (change.getTaskInfo() != null) {
+ if (isTask) {
// Skip non-tasks since those usually have null bounds.
startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
@@ -354,14 +362,34 @@
Animation a = loadAnimation(info, change, wallpaperTransit);
if (a != null) {
+ if (changeRequiresBackground(info, change)) {
+ requireBackgroundForTransition = true;
+ }
+
+ float cornerRadius = 0;
+ if (a.hasRoundedCorners() && isTask) {
+ // hasRoundedCorners is currently only enabled for tasks
+ final Context displayContext =
+ mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
+ cornerRadius =
+ ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+ }
+
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */);
+ mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
+ cornerRadius, change.getEndAbsBounds());
if (info.getAnimationOptions() != null) {
- attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+ attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
+ cornerRadius);
}
}
}
+
+ if (requireBackgroundForTransition) {
+ addBackgroundToTransition(info.getRootLeash(), startTransaction, finishTransaction);
+ }
+
startTransaction.apply();
TransitionMetrics.getInstance().reportAnimationStart(transition);
// run finish now in-case there are no animations
@@ -369,6 +397,40 @@
return true;
}
+ private boolean changeRequiresBackground(TransitionInfo info,
+ TransitionInfo.Change change) {
+ final boolean isTask = change.getTaskInfo() != null;
+ final @TransitionType int type = info.getType();
+ final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN || type == TRANSIT_CLOSE
+ || type == TRANSIT_TO_FRONT || type == TRANSIT_TO_BACK;
+ return isTask && isOpenOrCloseTransition;
+ }
+
+ private void addBackgroundToTransition(
+ @NonNull SurfaceControl rootLeash,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction
+ ) {
+ final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ final @ColorInt int overviewBackgroundColor =
+ uiContext.getColor(R.color.overview_background);
+ final Color bgColor = Color.valueOf(overviewBackgroundColor);
+ final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
+
+ final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
+ .setName("Animation Background")
+ .setParent(rootLeash)
+ .setColorLayer()
+ .setOpaque(true)
+ .build();
+
+ startTransaction
+ .setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
+ .setColor(animationBackgroundSurface, colorArray)
+ .show(animationBackgroundSurface);
+ finishTransaction.remove(animationBackgroundSurface);
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -508,7 +570,7 @@
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
- @Nullable Point position) {
+ @Nullable Point position, float cornerRadius, @Nullable Rect clipRect) {
final SurfaceControl.Transaction transaction = pool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
@@ -520,12 +582,12 @@
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
- position);
+ position, cornerRadius, clipRect);
});
final Runnable finisher = () -> {
applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
- position);
+ position, cornerRadius, clipRect);
pool.release(transaction);
mainExecutor.execute(() -> {
@@ -550,23 +612,24 @@
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
- TransitionInfo.AnimationOptions options) {
+ TransitionInfo.AnimationOptions options, float cornerRadius) {
final boolean isTask = change.getTaskInfo() != null;
final boolean isOpen = Transitions.isOpeningType(change.getMode());
final boolean isClose = Transitions.isClosingType(change.getMode());
if (isOpen) {
if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
- attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+ attachCrossProfileThunmbnailAnimation(animations, finishCallback, change,
+ cornerRadius);
} else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
- attachThumbnailAnimation(animations, finishCallback, change, options);
+ attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
}
} else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
- attachThumbnailAnimation(animations, finishCallback, change, options);
+ attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
}
}
private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull Runnable finishCallback, TransitionInfo.Change change) {
+ @NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) {
final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
final Rect bounds = change.getEndAbsBounds();
@@ -594,12 +657,13 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top));
+ mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top),
+ cornerRadius, change.getEndAbsBounds());
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
- TransitionInfo.AnimationOptions options) {
+ TransitionInfo.AnimationOptions options, float cornerRadius) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
change.getLeash(), options.getThumbnail(), transaction);
@@ -618,7 +682,8 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, null /* position */);
+ mMainExecutor, mAnimExecutor, null /* position */,
+ cornerRadius, change.getEndAbsBounds());
}
private static int getWallpaperTransitType(TransitionInfo info) {
@@ -650,13 +715,19 @@
private static void applyTransformation(long time, SurfaceControl.Transaction t,
SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
- Point position) {
+ Point position, float cornerRadius, @Nullable Rect clipRect) {
anim.getTransformation(time, transformation);
if (position != null) {
transformation.getMatrix().postTranslate(position.x, position.y);
}
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
+ if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
+ // We can only apply rounded corner if a crop is set
+ t.setWindowCrop(leash, clipRect);
+ t.setCornerRadius(leash, cornerRadius);
+ }
+
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
t.apply();
}
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 13c670a..45d8be1 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
@@ -292,14 +292,16 @@
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
@NonNull ShellExecutor animExecutor) {
startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ mTransactionPool, mainExecutor, animExecutor, null /* position */,
+ 0 /* cornerRadius */, null /* clipRect */);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
@NonNull ShellExecutor animExecutor) {
startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ mTransactionPool, mainExecutor, animExecutor, null /* position */,
+ 0 /* cornerRadius */, null /* clipRect */);
}
private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
index 74e4812..367676f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
@@ -26,10 +26,16 @@
*/
public interface ShellUnfoldProgressProvider {
+ // This is a temporary workaround until we move the progress providers into the Shell or
+ // refactor the dependencies. TLDR, the base module depends on this provider to determine if the
+ // FullscreenUnfoldController is available, but this check can't rely on an optional component.
+ public static final ShellUnfoldProgressProvider NO_PROVIDER =
+ new ShellUnfoldProgressProvider() {};
+
/**
* Adds a transition listener
*/
- void addListener(Executor executor, UnfoldListener listener);
+ default void addListener(Executor executor, UnfoldListener listener) {}
/**
* Listener for receiving unfold updates
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index ecc2d31..1d463d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -24,11 +24,13 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
@@ -82,7 +84,11 @@
@FlakyTest
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 04c82e5..10cf0b7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -24,10 +24,12 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,7 +69,11 @@
@FlakyTest
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index b7d3ba6..722ec34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -24,11 +24,13 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
@@ -86,7 +88,11 @@
@FlakyTest
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index f6ce3d4..38c008c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -25,10 +25,12 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,7 +73,11 @@
@FlakyTest
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 5fe13e0..a787f2b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -27,6 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -40,6 +41,7 @@
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -110,7 +112,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index a238bc2..6041e23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -30,6 +30,7 @@
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
@@ -44,6 +45,7 @@
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -134,7 +136,11 @@
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Test
fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 50cd548..e44d7d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,6 +25,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -34,6 +35,7 @@
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -77,7 +79,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 8d52225..d33d92b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,6 +25,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -34,6 +35,7 @@
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -76,7 +78,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 070f636..ece68df 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -24,6 +24,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -35,6 +36,7 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -85,7 +87,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index fabbd26..127301f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,6 +25,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -36,6 +37,7 @@
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,7 +93,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 33626d0..c439922 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -26,6 +26,8 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,6 +67,15 @@
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
/**
* Checks [pipApp] window remains visible throughout the animation
*/
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 791505b..f923a23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -27,6 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.traces.common.FlickerComponentName
@@ -35,6 +36,7 @@
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -119,7 +121,11 @@
*/
@Presubmit
@Test
- override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
/**
* Checks that all parts of the screen are covered at the start and end of the transition
@@ -204,6 +210,7 @@
@Presubmit
@Test
fun testAppPlusPipLayerCoversFullScreenOnEnd() {
+ // This test doesn't work in shell transitions because of b/206669574
testSpec.assertLayersEnd {
val pipRegion = visibleRegion(pipApp.component).region
visibleRegion(testApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 8267442..3e7e2f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,7 +18,9 @@
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import org.junit.Assume.assumeFalse
import org.junit.Test
/**
@@ -27,6 +29,15 @@
abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
protected val testApp = FixedAppHelper(instrumentation)
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 5f29dbc..4d63d14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,7 +24,10 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -75,6 +79,15 @@
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 00ccf26..030e040 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,7 +24,10 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -72,6 +76,15 @@
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index b0b11e9..2def979 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,7 +24,11 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
+import org.junit.Before
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -60,6 +65,21 @@
}
}
+ @Before
+ fun onBefore() {
+ // This CUJ don't work in shell transitions because of b/204570898 b/204562589
+ assumeFalse(isShellTransitionsEnabled)
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index f4eb701..9191d0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -25,7 +25,9 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -96,7 +98,11 @@
@Presubmit
@Test
- override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index f196764..3511cc2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -26,6 +26,9 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,6 +65,12 @@
}
}
+ @Before
+ fun onBefore() {
+ // This CUJ don't work in shell transitions because of b/204570898 b/204562589
+ assumeFalse(isShellTransitionsEnabled)
+ }
+
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
* animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index d9685f3..10a542f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,8 +24,11 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.traces.RegionSubject
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -78,6 +82,15 @@
current.isHigherOrEqual(previous.region)
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index c6b42ea..cb6ba0e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,8 +24,11 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.traces.RegionSubject
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -78,6 +82,15 @@
current.isLowerOrEqual(previous.region)
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 45cbdc8..81ac10f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -25,9 +25,11 @@
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,6 +72,15 @@
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
/**
* Ensure the pip window remains visible throughout any keyboard interactions
*/
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 3e3ea16..70075dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -89,6 +89,15 @@
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(com.android.server.wm.flicker.helpers.isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
@FlakyTest(bugId = 161435597)
@Test
fun pipWindowInsideDisplayBounds() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index af984b3..16fc048 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -27,10 +27,13 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import org.junit.Assume.assumeFalse
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,6 +84,12 @@
}
}
+ @Before
+ fun onBefore() {
+ // This CUJ don't work in shell transitions because of b/204570898 b/204562589 b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ }
+
/**
* Checks that all parts of the screen are covered at the start and end of the transition
*/
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index f8e2d38..9c26105 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -25,10 +25,12 @@
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -102,7 +104,11 @@
@FlakyTest
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index 31e9167..9c3b0fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -27,7 +27,7 @@
import com.android.wm.shell.flicker.pip.PipTestBase
import org.junit.After
import org.junit.Assert.assertFalse
-import org.junit.Assume
+import org.junit.Assume.assumeTrue
import org.junit.Before
abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATION_0) {
@@ -37,7 +37,7 @@
@Before
final override fun televisionSetUp() {
// Should run only on TVs.
- Assume.assumeTrue(isTelevision)
+ assumeTrue(isTelevision)
systemUiProcessObserver.start()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index c2f58b8..935f669 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -191,7 +191,7 @@
mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
displayId, new Configuration());
- verify(mMockPipMotionHelper).animateResizedBounds(any(Rect.class));
+ verify(mMockPipMotionHelper).movePip(any(Rect.class));
}
@Test
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 8f04cfb..6ea303c 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -17,24 +17,29 @@
#define LOG_TAG "PointerController"
//#define LOG_NDEBUG 0
-// Log debug messages about pointer updates
-#define DEBUG_POINTER_UPDATES 0
-
#include "PointerController.h"
-#include "MouseCursorController.h"
#include "PointerControllerContext.h"
-#include "TouchSpotController.h"
-#include <log/log.h>
-
-#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
-#include <SkPaint.h>
namespace android {
+namespace {
+
+const ui::Transform kIdentityTransform;
+
+} // namespace
+
+// --- PointerController::DisplayInfoListener ---
+
+void PointerController::DisplayInfoListener::onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>& displayInfo) {
+ mPointerController.onDisplayInfosChanged(displayInfo);
+}
+
// --- PointerController ---
std::shared_ptr<PointerController> PointerController::create(
@@ -63,9 +68,12 @@
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper,
const sp<SpriteController>& spriteController)
- : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
+ : mContext(policy, looper, spriteController, *this),
+ mCursorController(mContext),
+ mDisplayInfoListener(new DisplayInfoListener(*this)) {
std::scoped_lock lock(mLock);
mLocked.presentation = Presentation::SPOT;
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(mDisplayInfoListener);
}
bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -74,7 +82,14 @@
}
void PointerController::move(float deltaX, float deltaY) {
- mCursorController.move(deltaX, deltaY);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+ }
+ mCursorController.move(transformed.x, transformed.y);
}
void PointerController::setButtonState(int32_t buttonState) {
@@ -86,12 +101,26 @@
}
void PointerController::setPosition(float x, float y) {
- std::scoped_lock lock(mLock);
- mCursorController.setPosition(x, y);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transform.transform(x, y);
+ }
+ mCursorController.setPosition(transformed.x, transformed.y);
}
void PointerController::getPosition(float* outX, float* outY) const {
+ const int32_t displayId = mCursorController.getDisplayId();
mCursorController.getPosition(outX, outY);
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ const auto xy = transform.inverse().transform(*outX, *outY);
+ *outX = xy.x;
+ *outY = xy.y;
+ }
}
int32_t PointerController::getDisplayId() const {
@@ -130,11 +159,25 @@
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
std::scoped_lock lock(mLock);
+ std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+ const ui::Transform& transform = getTransformForDisplayLocked(displayId);
+
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+
+ const vec2 xy = transform.transform(spotCoords[index].getXYValue());
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ }
+
auto it = mLocked.spotControllers.find(displayId);
if (it == mLocked.spotControllers.end()) {
mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
}
- mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
+ mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits);
}
void PointerController::clearSpots() {
@@ -194,7 +237,7 @@
void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
std::unordered_set<int32_t> displayIdSet;
- for (DisplayViewport viewport : viewports) {
+ for (const DisplayViewport& viewport : viewports) {
displayIdSet.insert(viewport.displayId);
}
@@ -214,4 +257,17 @@
}
}
+void PointerController::onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfo) {
+ std::scoped_lock lock(mLock);
+ mLocked.mDisplayInfos = displayInfo;
+}
+
+const ui::Transform& PointerController::getTransformForDisplayLocked(int displayId) const {
+ const auto& di = mLocked.mDisplayInfos;
+ auto it = std::find_if(di.begin(), di.end(), [displayId](const gui::DisplayInfo& info) {
+ return info.displayId == displayId;
+ });
+ return it != di.end() ? it->transform : kIdentityTransform;
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 97567ba..58bb014 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -72,6 +72,8 @@
void reloadPointerResources();
void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
+ void onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfos);
+
private:
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -85,9 +87,23 @@
struct Locked {
Presentation presentation;
+ std::vector<gui::DisplayInfo> mDisplayInfos;
std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
} mLocked GUARDED_BY(mLock);
+ class DisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit DisplayInfoListener(PointerController& pc) : mPointerController(pc){};
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>&) override;
+
+ private:
+ PointerController& mPointerController;
+ };
+ sp<DisplayInfoListener> mDisplayInfoListener;
+
+ const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(mLock);
+
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
const sp<SpriteController>& spriteController);
void clearSpotsLocked();
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index 9577281..cd0bdea 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -19,4 +19,8 @@
],
sdk_version: "system_current",
min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.cellbroadcast",
+ ],
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 71bf9f6..a781a62 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -175,9 +175,12 @@
public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
Drawable badge = null;
if (userId != UserHandle.USER_NULL) {
- boolean isManaged = context.getSystemService(DevicePolicyManager.class)
- .getProfileOwnerAsUser(userId) != null;
- if (isManaged) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ boolean isCorp =
+ dpm.getProfileOwnerAsUser(userId) != null // has an owner
+ && dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(UserHandle.of(userId))
+ == null; // and has no supervisor
+ if (isCorp) {
badge = getDrawableForDisplayDensity(
context, com.android.internal.R.drawable.ic_corp_badge_case);
}
diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
index ef60a24..c4cb89f 100644
--- a/packages/SystemUI/animation/res/values/ids.xml
+++ b/packages/SystemUI/animation/res/values/ids.xml
@@ -16,4 +16,5 @@
-->
<resources>
<item type="id" name="launch_animation_running"/>
+ <item type="id" name="dialog_content_parent" />
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 413612f..9aad278 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -40,6 +40,7 @@
import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
+private val DIALOG_CONTENT_PARENT_ID = R.id.dialog_content_parent
/**
* A class that allows dialogs to be started in a seamless way from a view that is transforming
@@ -86,10 +87,10 @@
// If the parent of the view we are launching from is the background of some other animated
// dialog, then this means the caller intent is to launch a dialog from another dialog. In
// this case, we also animate the parent (which is the dialog background).
- val dialogContentParent = openedDialogs
+ val animatedParent = openedDialogs
.firstOrNull { it.dialogContentParent == view.parent }
- ?.dialogContentParent
- val animateFrom = dialogContentParent ?: view
+ val parentHostDialog = animatedParent?.hostDialog
+ val animateFrom = animatedParent?.dialogContentParent ?: view
// Make sure we don't run the launch animation from the same view twice at the same time.
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
@@ -100,12 +101,18 @@
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
- val launchAnimation = AnimatedDialog(
- context, launchAnimator, hostDialogProvider, animateFrom,
- onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog,
- animateBackgroundBoundsChange)
- val hostDialog = launchAnimation.hostDialog
- openedDialogs.add(launchAnimation)
+ val animatedDialog = AnimatedDialog(
+ context,
+ launchAnimator,
+ hostDialogProvider,
+ animateFrom,
+ onDialogDismissed = { openedDialogs.remove(it) },
+ originalDialog = dialog,
+ animateBackgroundBoundsChange,
+ openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
+ )
+ val hostDialog = animatedDialog.hostDialog
+ openedDialogs.add(animatedDialog)
// If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
// host dialog.
@@ -119,15 +126,15 @@
// If AOD is disabled the screen will directly becomes black and we won't see
// the animation anyways.
if (reason == DialogListener.DismissReason.DEVICE_LOCKED) {
- launchAnimation.exitAnimationDisabled = true
+ animatedDialog.exitAnimationDisabled = true
}
hostDialog.dismiss()
}
override fun onHide() {
- if (launchAnimation.ignoreNextCallToHide) {
- launchAnimation.ignoreNextCallToHide = false
+ if (animatedDialog.ignoreNextCallToHide) {
+ animatedDialog.ignoreNextCallToHide = false
return
}
@@ -138,21 +145,44 @@
hostDialog.show()
// We don't actually want to show the original dialog, so hide it.
- launchAnimation.ignoreNextCallToHide = true
+ animatedDialog.ignoreNextCallToHide = true
dialog.hide()
}
override fun onSizeChanged() {
- launchAnimation.onOriginalDialogSizeChanged()
+ animatedDialog.onOriginalDialogSizeChanged()
+ }
+
+ override fun prepareForStackDismiss() {
+ animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
}
})
}
- launchAnimation.start()
+ animatedDialog.start()
return hostDialog
}
/**
+ * Launch [dialog] from a [parentHostDialog] as returned by [showFromView]. This will allow
+ * for dismissing the whole stack.
+ *
+ * This will return a new host dialog, with the same caveat as [showFromView].
+ *
+ * @see DialogListener.prepareForStackDismiss
+ */
+ fun showFromDialog(
+ dialog: Dialog,
+ parentHostDialog: Dialog,
+ animateBackgroundBoundsChange: Boolean = false
+ ): Dialog {
+ val view = parentHostDialog.findViewById<ViewGroup>(DIALOG_CONTENT_PARENT_ID)
+ ?.getChildAt(0)
+ ?: throw IllegalStateException("No dialog content parent found in host dialog")
+ return showFromView(dialog, view, animateBackgroundBoundsChange)
+ }
+
+ /**
* Ensure that all dialogs currently shown won't animate into their touch surface when
* dismissed.
*
@@ -214,6 +244,12 @@
/** Called when this dialog show() is called. */
fun onShow()
+ /**
+ * Call before dismissing a stack of dialogs (dialogs launched from dialogs), so the topmost
+ * can animate directly into the original `touchSurface`.
+ */
+ fun prepareForStackDismiss()
+
/** Called when this dialog size might have changed, e.g. because of configuration changes. */
fun onSizeChanged()
}
@@ -224,7 +260,7 @@
hostDialogProvider: HostDialogProvider,
/** The view that triggered the dialog after being tapped. */
- private val touchSurface: View,
+ var touchSurface: View,
/**
* A callback that will be called with this [AnimatedDialog] after the dialog was
@@ -236,7 +272,10 @@
private val originalDialog: Dialog,
/** Whether we should animate the dialog background when its bounds change. */
- private val animateBackgroundBoundsChange: Boolean
+ private val animateBackgroundBoundsChange: Boolean,
+
+ /** Launch animation corresponding to the parent [hostDialog]. */
+ private val parentAnimatedDialog: AnimatedDialog? = null
) {
/**
* The fullscreen dialog to which we will add the content view [originalDialogView] of
@@ -253,7 +292,9 @@
* the same size as the original dialog window and to which we will set the original dialog
* window background.
*/
- val dialogContentParent = FrameLayout(context)
+ val dialogContentParent = FrameLayout(context).apply {
+ id = DIALOG_CONTENT_PARENT_ID
+ }
/**
* The background color of [originalDialogView], taking into consideration the [originalDialog]
@@ -359,9 +400,7 @@
// Make the touch surface invisible and make sure that it stays invisible as long as the
// dialog is shown or animating.
touchSurface.visibility = View.INVISIBLE
- if (touchSurface is LaunchableView) {
- touchSurface.setShouldBlockVisibilityChanges(true)
- }
+ (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
// Add a pre draw listener to (maybe) start the animation once the touch surface is
// actually invisible.
@@ -576,9 +615,7 @@
Log.i(TAG, "Skipping animation of dialog into the touch surface")
// Make sure we allow the touch surface to change its visibility again.
- if (touchSurface is LaunchableView) {
- touchSurface.setShouldBlockVisibilityChanges(false)
- }
+ (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
// If the view is invisible it's probably because of us, so we make it visible again.
if (touchSurface.visibility == View.INVISIBLE) {
@@ -598,9 +635,7 @@
},
onLaunchAnimationEnd = {
// Make sure we allow the touch surface to change its visibility again.
- if (touchSurface is LaunchableView) {
- touchSurface.setShouldBlockVisibilityChanges(false)
- }
+ (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
touchSurface.visibility = View.VISIBLE
dialogContentParent.visibility = View.INVISIBLE
@@ -796,4 +831,18 @@
animator.start()
}
}
+
+ fun prepareForStackDismiss(): View {
+ if (parentAnimatedDialog == null) {
+ return touchSurface
+ }
+ parentAnimatedDialog.exitAnimationDisabled = true
+ parentAnimatedDialog.originalDialog.hide()
+ val view = parentAnimatedDialog.prepareForStackDismiss()
+ parentAnimatedDialog.originalDialog.dismiss()
+ // Make the touch surface invisible, so we end up animating to it when we actually
+ // dismiss the stack
+ view.visibility = View.INVISIBLE
+ return view
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index cdf770f..b533ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -20,6 +22,7 @@
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
@@ -533,6 +536,11 @@
private final ViewPager.OnPageChangeListener mOnPageChangeListener =
new ViewPager.SimpleOnPageChangeListener() {
+
+ private int mCurrentScrollState = SCROLL_STATE_IDLE;
+ // Flag to avoid redundant call InteractionJankMonitor::begin()
+ private boolean mIsScrollJankTraceBegin = false;
+
@Override
public void onPageSelected(int position) {
updateSelected();
@@ -546,6 +554,13 @@
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
+
+ if (!mIsScrollJankTraceBegin && mCurrentScrollState == SCROLL_STATE_DRAGGING) {
+ InteractionJankMonitor.getInstance().begin(PagedTileLayout.this,
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE);
+ mIsScrollJankTraceBegin = true;
+ }
+
if (mPageIndicator == null) return;
mPageIndicatorPosition = position + positionOffset;
mPageIndicator.setLocation(mPageIndicatorPosition);
@@ -554,6 +569,16 @@
(isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
}
}
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (state != mCurrentScrollState && state == SCROLL_STATE_IDLE) {
+ InteractionJankMonitor.getInstance().end(
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE);
+ mIsScrollJankTraceBegin = false;
+ }
+ mCurrentScrollState = state;
+ }
};
private final PagerAdapter mAdapter = new PagerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index b718ebf..f793a50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -29,6 +29,8 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -38,10 +40,10 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
+import com.android.systemui.qs.user.UserSwitchDialogController;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import java.util.function.Consumer;
-
import javax.inject.Inject;
/**
@@ -78,7 +80,7 @@
private View mCurrentUserView;
private final UiEventLogger mUiEventLogger;
private final FalsingManager mFalsingManager;
- private Consumer<UserSwitcherController.UserRecord> mClickCallback;
+ private @Nullable UserSwitchDialogController.DialogShower mDialogShower;
@Inject
public Adapter(Context context, UserSwitcherController controller,
@@ -96,8 +98,17 @@
return createUserDetailItemView(convertView, parent, item);
}
- public void injectCallback(Consumer<UserSwitcherController.UserRecord> clickCallback) {
- mClickCallback = clickCallback;
+ /**
+ * If this adapter is inside a dialog, passing a
+ * {@link UserSwitchDialogController.DialogShower} will help animate to and from the parent
+ * dialog. This will also allow for dismissing the whole stack of dialogs in a single
+ * animation.
+ *
+ * @param shower
+ * @see SystemUIDialog#dismissStack()
+ */
+ public void injectDialogShower(UserSwitchDialogController.DialogShower shower) {
+ mDialogShower = shower;
}
public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
@@ -174,12 +185,9 @@
}
view.setActivated(true);
}
- onUserListItemClicked(tag);
+ onUserListItemClicked(tag, mDialogShower);
}
Trace.endSection();
- if (mClickCallback != null) {
- mClickCallback.accept(tag);
- }
}
public void linkToViewGroup(ViewGroup viewGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 563c4cd..77b9cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -306,12 +306,8 @@
final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
updateWifiToggle(isWifiEnabled, isDeviceLocked);
updateConnectedWifi(isWifiEnabled, isDeviceLocked);
+ updateWifiListAndSeeAll(isWifiEnabled, isDeviceLocked);
updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
-
- final int visibility = (isDeviceLocked || !isWifiEnabled || mWifiEntriesCount <= 0)
- ? View.GONE : View.VISIBLE;
- mWifiRecyclerView.setVisibility(visibility);
- mSeeAllLayout.setVisibility(visibility);
}
private void setOnClickListener() {
@@ -414,6 +410,18 @@
}
@MainThread
+ private void updateWifiListAndSeeAll(boolean isWifiEnabled, boolean isDeviceLocked) {
+ if (!isWifiEnabled || isDeviceLocked) {
+ mWifiRecyclerView.setVisibility(View.GONE);
+ mSeeAllLayout.setVisibility(View.GONE);
+ return;
+ }
+ mWifiRecyclerView.setVisibility(mWifiEntriesCount > 0 ? View.VISIBLE : View.GONE);
+ mSeeAllLayout.setVisibility(
+ (mConnectedWifiEntry != null || mWifiEntriesCount > 0) ? View.VISIBLE : View.GONE);
+ }
+
+ @MainThread
private void updateWifiScanNotify(boolean isWifiEnabled, boolean isWifiScanEnabled,
boolean isDeviceLocked) {
if (isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index bae7996..d74a50e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -16,7 +16,9 @@
package com.android.systemui.qs.user
+import android.app.Dialog
import android.content.Context
+import android.content.DialogInterface
import android.content.Intent
import android.provider.Settings
import android.view.View
@@ -84,12 +86,26 @@
doneButton.setOnClickListener { dismiss() }
val adapter = userDetailViewAdapterProvider.get()
- adapter.injectCallback {
- dismiss()
- }
adapter.linkToViewGroup(grid)
- dialogLaunchAnimator.showFromView(this, view)
+ val hostDialog = dialogLaunchAnimator.showFromView(this, view)
+ adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator))
}
}
+
+ private class DialogShowerImpl(
+ private val hostDialog: Dialog,
+ private val dialogLaunchAnimator: DialogLaunchAnimator
+ ) : DialogInterface by hostDialog, DialogShower {
+ override fun showDialog(dialog: Dialog): Dialog {
+ return dialogLaunchAnimator.showFromDialog(
+ dialog,
+ parentHostDialog = hostDialog
+ )
+ }
+ }
+
+ interface DialogShower : DialogInterface {
+ fun showDialog(dialog: Dialog): Dialog
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index cf4aaba..1130ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -218,6 +218,19 @@
}
}
+ /**
+ * Dismiss this dialog. If it was launched from another dialog using
+ * {@link com.android.systemui.animation.DialogLaunchAnimator#showFromView} with a
+ * non-{@code null} {@code parentHostDialog} parameter, also dismisses the stack of dialogs,
+ * animating back to the original touchSurface.
+ */
+ public void dismissStack() {
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.prepareForStackDismiss();
+ }
+ dismiss();
+ }
+
@Override
public void hide() {
super.hide();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 0443d94..364c931 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -55,6 +55,8 @@
import android.view.WindowManagerGlobal;
import android.widget.BaseAdapter;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
@@ -77,6 +79,7 @@
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -460,7 +463,7 @@
}
@VisibleForTesting
- void onUserListItemClicked(UserRecord record) {
+ void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
@@ -472,7 +475,7 @@
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
id = guestId;
} else if (record.isAddUser) {
- showAddUserDialog();
+ showAddUserDialog(dialogShower);
return;
} else {
id = record.info.id;
@@ -481,7 +484,7 @@
int currUserId = mUserTracker.getUserId();
if (currUserId == id) {
if (record.isGuest) {
- showExitGuestDialog(id);
+ showExitGuestDialog(id, dialogShower);
}
return;
}
@@ -490,11 +493,15 @@
// If switching from guest, we want to bring up the guest exit dialog instead of switching
UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
if (currUserInfo != null && currUserInfo.isGuest()) {
- showExitGuestDialog(currUserId, record.resolveId());
+ showExitGuestDialog(currUserId, record.resolveId(), dialogShower);
return;
}
}
-
+ if (dialogShower != null) {
+ // If we haven't morphed into another dialog, it means we have just switched users.
+ // Then, dismiss the dialog.
+ dialogShower.dismiss();
+ }
switchToUserId(id);
}
@@ -511,7 +518,7 @@
}
}
- protected void showExitGuestDialog(int id) {
+ private void showExitGuestDialog(int id, DialogShower dialogShower) {
int newId = UserHandle.USER_SYSTEM;
if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
@@ -519,23 +526,31 @@
newId = info.id;
}
}
- showExitGuestDialog(id, newId);
+ showExitGuestDialog(id, newId, dialogShower);
}
- protected void showExitGuestDialog(int id, int targetId) {
+ private void showExitGuestDialog(int id, int targetId, DialogShower dialogShower) {
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
mExitGuestDialog.cancel();
}
mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
- mExitGuestDialog.show();
+ if (dialogShower != null) {
+ dialogShower.showDialog(mExitGuestDialog);
+ } else {
+ mExitGuestDialog.show();
+ }
}
- public void showAddUserDialog() {
+ private void showAddUserDialog(DialogShower dialogShower) {
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
mAddUserDialog.cancel();
}
mAddUserDialog = new AddUserDialog(mContext);
- mAddUserDialog.show();
+ if (dialogShower != null) {
+ dialogShower.showDialog(mAddUserDialog);
+ } else {
+ mAddUserDialog.show();
+ }
}
private void listenForCallState() {
@@ -868,9 +883,17 @@
/**
* It handles click events on user list items.
+ *
+ * If the user switcher is hosted in a dialog, passing a non-null {@link DialogShower}
+ * will allow animation to and from the parent dialog.
+ *
*/
+ public void onUserListItemClicked(UserRecord record, @Nullable DialogShower dialogShower) {
+ mController.onUserListItemClicked(record, dialogShower);
+ }
+
public void onUserListItemClicked(UserRecord record) {
- mController.onUserListItemClicked(record);
+ onUserListItemClicked(record, null);
}
public String getName(Context context, UserRecord item) {
@@ -1156,7 +1179,7 @@
cancel();
} else {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
- dismiss();
+ dismissStack();
removeGuestUser(mGuestId, mTargetId);
}
}
@@ -1187,7 +1210,7 @@
if (which == BUTTON_NEGATIVE) {
cancel();
} else {
- dismiss();
+ dismissStack();
if (ActivityManager.isUserAMonkey()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 6d176a7..50a0b80 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -176,6 +176,34 @@
? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM;
}
+ private boolean isSeedColorSet(JSONObject jsonObject, WallpaperColors newWallpaperColors) {
+ if (newWallpaperColors == null) {
+ return false;
+ }
+ // Gets the color that was overridden in the theme setting if any.
+ String sysPaletteColor = (String) jsonObject.opt(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ if (sysPaletteColor == null) {
+ return false;
+ }
+ if (!sysPaletteColor.startsWith("#")) {
+ sysPaletteColor = "#" + sysPaletteColor;
+ }
+ final int systemPaletteColorArgb = Color.parseColor(sysPaletteColor);
+ // Gets seed colors from incoming {@link WallpaperColors} instance.
+ List<Integer> seedColors = ColorScheme.getSeedColors(newWallpaperColors);
+ for (int seedColor : seedColors) {
+ // The seed color from incoming {@link WallpaperColors} instance
+ // was set as color override.
+ if (seedColor == systemPaletteColorArgb) {
+ if (DEBUG) {
+ Log.d(TAG, "Same as previous set system palette: " + sysPaletteColor);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags) {
final boolean hadWallpaperColors = mCurrentColors != null;
int latestWallpaperType = getLatestWallpaperType();
@@ -213,8 +241,11 @@
try {
JSONObject jsonObject = (overlayPackageJson == null) ? new JSONObject()
: new JSONObject(overlayPackageJson);
+ // The latest applied wallpaper should be the source of system colors when:
+ // There is not preset color applied and the incoming wallpaper color is not applied
if (!COLOR_SOURCE_PRESET.equals(jsonObject.optString(OVERLAY_COLOR_SOURCE))
- && ((flags & latestWallpaperType) != 0)) {
+ && ((flags & latestWallpaperType) != 0 && !isSeedColorSet(jsonObject,
+ wallpaperColors))) {
mSkipSettingChange = true;
if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index cebc931..cd3e2d3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -100,11 +100,11 @@
fun provideShellProgressProvider(
config: UnfoldTransitionConfig,
provider: Optional<UnfoldTransitionProgressProvider>
- ): Optional<ShellUnfoldProgressProvider> =
+ ): ShellUnfoldProgressProvider =
if (config.isEnabled && provider.isPresent()) {
- Optional.of(UnfoldProgressProvider(provider.get()))
+ UnfoldProgressProvider(provider.get())
} else {
- Optional.empty()
+ ShellUnfoldProgressProvider.NO_PROVIDER
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index d4c3840..9bd33eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -16,6 +16,7 @@
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,24 +29,22 @@
private val dialogLaunchAnimator =
DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
+ private val attachedViews = mutableSetOf<View>()
+
+ @After
+ fun tearDown() {
+ runOnMainThreadAndWaitForIdleSync {
+ attachedViews.forEach {
+ ViewUtils.detachView(it)
+ }
+ }
+ }
+
@Test
fun testShowDialogFromView() {
// Show the dialog. showFromView() must be called on the main thread with a dialog created
// on the main thread too.
- val (dialog, hostDialog) = runOnMainThreadAndWaitForIdleSync {
- val touchSurfaceRoot = LinearLayout(context)
- val touchSurface = View(context)
- touchSurfaceRoot.addView(touchSurface)
-
- // We need to attach the root to the window manager otherwise the exit animation will
- // be skipped
- ViewUtils.attachView(touchSurfaceRoot)
-
- val dialog = TestDialog(context)
- val hostDialog =
- dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
- dialog to hostDialog
- }
+ val (dialog, hostDialog) = createDialogAndHostDialog()
// Only the host dialog is actually showing.
assertTrue(hostDialog.isShowing)
@@ -100,6 +99,51 @@
assertTrue(dialog.onStopCalled)
}
+ @Test
+ fun testStackedDialogsDismissesAll() {
+ val (_, hostDialogFirst) = createDialogAndHostDialog()
+ val (dialogSecond, hostDialogSecond) = createDialogAndHostDialogFromDialog(hostDialogFirst)
+
+ runOnMainThreadAndWaitForIdleSync {
+ dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
+ dialogSecond.dismissStack()
+ }
+
+ assertTrue(hostDialogSecond.wasDismissed)
+ assertTrue(hostDialogFirst.wasDismissed)
+ }
+
+ private fun createDialogAndHostDialog(): Pair<TestDialog, TestHostDialog> {
+ return runOnMainThreadAndWaitForIdleSync {
+ val touchSurfaceRoot = LinearLayout(context)
+ val touchSurface = View(context)
+ touchSurfaceRoot.addView(touchSurface)
+
+ // We need to attach the root to the window manager otherwise the exit animation will
+ // be skipped
+ ViewUtils.attachView(touchSurfaceRoot)
+ attachedViews.add(touchSurfaceRoot)
+
+ val dialog = TestDialog(context)
+ val hostDialog =
+ dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
+ dialog to hostDialog
+ }
+ }
+
+ private fun createDialogAndHostDialogFromDialog(
+ hostParent: Dialog
+ ): Pair<TestDialog, TestHostDialog> {
+ return runOnMainThreadAndWaitForIdleSync {
+ val dialog = TestDialog(context)
+ val hostDialog = dialogLaunchAnimator.showFromDialog(
+ dialog,
+ hostParent
+ ) as TestHostDialog
+ dialog to hostDialog
+ }
+ }
+
private fun <T : Any> runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
lateinit var result: T
context.mainExecutor.execute {
@@ -198,6 +242,11 @@
notifyListeners { onShow() }
}
+ fun dismissStack() {
+ notifyListeners { prepareForStackDismiss() }
+ dismiss()
+ }
+
private fun notifyListeners(notify: DialogListener.() -> Unit) {
for (listener in HashSet(listeners)) {
listener.notify()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b6e8979..b32b4d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -219,61 +219,89 @@
}
@Test
- public void updateDialog_wifiOnAndNoWifiList_hideWifiListAndSeeAll() {
+ public void updateDialog_wifiOnAndNoWifiEntry_hideWifiEntryAndSeeAll() {
// The precondition WiFi ON is already in setUp()
+ mInternetDialog.mConnectedWifiEntry = null;
mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.updateDialog(false);
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
}
@Test
- public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+ public void updateDialog_wifiOnAndHasConnectedWifi_showConnectedWifiAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
+ mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.updateDialog(false);
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ mInternetDialog.mConnectedWifiEntry = null;
+
+ mInternetDialog.updateDialog(false);
+
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
- public void updateDialog_deviceLockedAndHasInternetWifi_showHighlightWifiToggle() {
- // The preconditions WiFi ON and Internet WiFi are already in setUp()
- when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+ public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.updateDialog(false);
- assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiToggle.getBackground()).isNotNull();
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
- public void updateDialog_deviceLockedAndHasInternetWifi_hideConnectedWifi() {
- // The preconditions WiFi ON and Internet WiFi are already in setUp()
- when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
-
- mInternetDialog.updateDialog(false);
-
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void updateDialog_deviceLockedAndHasWifiList_hideWifiListAndSeeAll() {
+ public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
// The preconditions WiFi entries are already in setUp()
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+ mInternetDialog.mConnectedWifiEntry = null;
mInternetDialog.updateDialog(false);
+ // Show WiFi Toggle without background
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiToggle.getBackground()).isNull();
+ // Hide Wi-Fi networks and See all
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+ mInternetDialog.updateDialog(false);
+
+ // Show WiFi Toggle with highlight background
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiToggle.getBackground()).isNotNull();
+ // Hide Wi-Fi networks and See all
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void updateDialog_wifiOn_hideWifiScanNotify() {
- // The preconditions WiFi ON and Internet WiFi are already in setUp()
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.updateDialog(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 7e900c8..ea3a42c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.user
+import android.app.Dialog
import android.content.Intent
import android.provider.Settings
import android.testing.AndroidTestingRunner
@@ -27,7 +28,7 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PseudoGridView
import com.android.systemui.qs.tiles.UserDetailView
-import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -39,15 +40,13 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.argThat
import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -71,6 +70,8 @@
private lateinit var gridView: PseudoGridView
@Mock
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock
+ private lateinit var hostDialog: Dialog
@Captor
private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
@@ -85,6 +86,8 @@
`when`(dialog.grid).thenReturn(gridView)
`when`(launchView.context).thenReturn(mContext)
+ `when`(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean()))
+ .thenReturn(hostDialog)
controller = UserSwitchDialogController(
{ userDetailViewAdapter },
@@ -188,15 +191,15 @@
}
@Test
- fun callbackFromDetailView_dismissesDialog() {
- val captor = argumentCaptor<Consumer<UserSwitcherController.UserRecord>>()
+ fun callbackFromDialogShower_dismissesDialog() {
+ val captor = argumentCaptor<UserSwitchDialogController.DialogShower>()
controller.showDialog(launchView)
- verify(userDetailViewAdapter).injectCallback(capture(captor))
+ verify(userDetailViewAdapter).injectDialogShower(capture(captor))
- captor.value.accept(mock(UserSwitcherController.UserRecord::class.java))
+ captor.value.dismiss()
- verify(dialog).dismiss()
+ verify(hostDialog).dismiss()
}
private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 379c595..a39d580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -44,13 +44,16 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.QSUserSwitcherEvent
+import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView
import com.android.systemui.telephony.TelephonyListenerManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -85,6 +88,8 @@
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock private lateinit var latencyTracker: LatencyTracker
+ @Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
+ @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
private lateinit var testableLooper: TestableLooper
private lateinit var uiBgExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -98,6 +103,8 @@
private val guestId = 1234
private val guestInfo = UserInfo(guestId, "Guest", null,
UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
+ private val secondaryUser =
+ UserInfo(10, "Secondary", null, 0, UserManager.USER_TYPE_FULL_SECONDARY)
@Before
fun setUp() {
@@ -114,6 +121,7 @@
mock(FingerprintManager::class.java))
`when`(userManager.canAddMoreUsers()).thenReturn(true)
+ `when`(notificationShadeWindowView.context).thenReturn(context)
userSwitcherController = UserSwitcherController(
context,
@@ -139,6 +147,26 @@
userSwitcherController.mPauseRefreshUsers = true
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
+ userSwitcherController.init(notificationShadeWindowView)
+ }
+
+ @Test
+ fun testSwitchUser_parentDialogDismissed() {
+ val otherUserRecord = UserSwitcherController.UserRecord(
+ secondaryUser,
+ picture,
+ false /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(ownerId)
+ `when`(userTracker.userInfo).thenReturn(ownerInfo)
+
+ userSwitcherController.onUserListItemClicked(otherUserRecord, dialogShower)
+ testableLooper.processAllMessages()
+
+ verify(dialogShower).dismiss()
}
@Test
@@ -156,7 +184,7 @@
`when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
- userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
+ userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
testableLooper.processAllMessages()
verify(interactionJankMonitor).begin(any())
verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
@@ -166,6 +194,26 @@
}
@Test
+ fun testAddGuest_parentDialogDismissed() {
+ val emptyGuestUserRecord = UserSwitcherController.UserRecord(
+ null,
+ null,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(ownerId)
+ `when`(userTracker.userInfo).thenReturn(ownerInfo)
+
+ `when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
+
+ userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
+ testableLooper.processAllMessages()
+ verify(dialogShower).dismiss()
+ }
+
+ @Test
fun testRemoveGuest_removeButtonPressed_isLogged() {
val currentGuestUserRecord = UserSwitcherController.UserRecord(
guestInfo,
@@ -178,7 +226,7 @@
`when`(userTracker.userId).thenReturn(guestInfo.id)
`when`(userTracker.userInfo).thenReturn(guestInfo)
- userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
assertNotNull(userSwitcherController.mExitGuestDialog)
userSwitcherController.mExitGuestDialog
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
@@ -188,6 +236,46 @@
}
@Test
+ fun testRemoveGuest_removeButtonPressed_dialogDismissed() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ true /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(guestInfo.id)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
+ assertNotNull(userSwitcherController.mExitGuestDialog)
+ userSwitcherController.mExitGuestDialog
+ .getButton(DialogInterface.BUTTON_POSITIVE).performClick()
+ testableLooper.processAllMessages()
+ assertFalse(userSwitcherController.mExitGuestDialog.isShowing)
+ }
+
+ @Test
+ fun testRemoveGuest_dialogShowerUsed() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ true /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+ `when`(userTracker.userId).thenReturn(guestInfo.id)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord, dialogShower)
+ assertNotNull(userSwitcherController.mExitGuestDialog)
+ testableLooper.processAllMessages()
+ verify(dialogShower).showDialog(userSwitcherController.mExitGuestDialog)
+ }
+
+ @Test
fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
val currentGuestUserRecord = UserSwitcherController.UserRecord(
guestInfo,
@@ -200,7 +288,7 @@
`when`(userTracker.userId).thenReturn(guestId)
`when`(userTracker.userInfo).thenReturn(guestInfo)
- userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
assertNotNull(userSwitcherController.mExitGuestDialog)
userSwitcherController.mExitGuestDialog
.getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
@@ -226,7 +314,7 @@
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
.thenReturn(1)
- userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
// Simulate a user switch event
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
@@ -260,7 +348,7 @@
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
.thenReturn(1)
- userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
// Simulate a user switch event
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 766471b..3ff5666 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -248,8 +248,9 @@
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
@@ -274,14 +275,15 @@
}
@Test
- public void onWallpaperColorsChanged_ResetThemeWithDifferentWallpapers() {
+ public void onWallpaperColorsChanged_ResetThemeWithNewHomeWallpapers() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
@@ -304,14 +306,15 @@
}
@Test
- public void onWallpaperColorsChanged_ResetThemeWithSameWallpaper() {
+ public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
@@ -339,8 +342,9 @@
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
@@ -366,8 +370,9 @@
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"lock_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
@@ -394,8 +399,9 @@
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
@@ -416,6 +422,36 @@
}
@Test
+ public void onWallpaperColorsChanged_keepThemeWhenFromLatestWallpaperAndSpecifiedColor() {
+ // Shouldn't ask for a new theme when the colors of the last applied wallpaper change
+ // with the same specified system palette one.
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(0xffa16b00), null);
+
+ String jsonString =
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ + "\"android.theme.customization.color_index\":\"2\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+ when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+ // SYSTEM wallpaper is the last applied one
+ when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)).thenReturn(2);
+
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+
+ ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+ verify(mSecureSettings, never()).putString(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+
+ // Apply overlay by existing theme from secure setting
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onWallpaperColorsChanged_keepThemeIfNotLatestWallpaper() {
// Shouldn't ask for a new theme when the colors of the wallpaper that is not the last
// applied one change
@@ -423,8 +459,9 @@
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\","
- + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+ "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+ + "\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.accent_color\":\"A16B00\","
+ "\"android.theme.customization.color_index\":\"2\"}";
when(mSecureSettings.getStringForUser(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 311555e..6f009b6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3713,8 +3713,19 @@
}
@Override
- public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
- final int userId = UserHandle.getUserId(uid);
+ public StorageVolume[] getVolumeList(int userId, String callingPackage, int flags) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ if (!isUidOwnerOfPackageOrSystem(callingPackage, callingUid)) {
+ throw new SecurityException("callingPackage does not match UID");
+ }
+ if (callingUserId != userId) {
+ // Callers can ask for volumes of different users, but only with the correct permissions
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ "Need INTERACT_ACROSS_USERS to get volumes for another user");
+ }
final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
@@ -3730,7 +3741,7 @@
// should never attempt to augment the actual storage volume state,
// otherwise we risk confusing it with race conditions as users go
// through various unlocked states
- final boolean callerIsMediaStore = UserHandle.isSameApp(Binder.getCallingUid(),
+ final boolean callerIsMediaStore = UserHandle.isSameApp(callingUid,
mMediaStoreAuthorityAppId);
final boolean userIsDemo;
@@ -3740,8 +3751,9 @@
try {
userIsDemo = LocalServices.getService(UserManagerInternal.class)
.getUserInfo(userId).isDemo();
+ storagePermission = mStorageManagerInternal.hasExternalStorage(callingUid,
+ callingPackage);
userKeyUnlocked = isUserKeyUnlocked(userId);
- storagePermission = mStorageManagerInternal.hasExternalStorage(uid, packageName);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a075a13..d52f52e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -103,6 +103,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
@@ -162,6 +163,7 @@
static final int USER_UNLOCKED_MSG = 105;
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
static final int START_USER_SWITCH_FG_MSG = 120;
+ static final int COMPLETE_USER_SWITCH_MSG = 130;
// Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
// the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
@@ -385,6 +387,7 @@
@VisibleForTesting
UserController(Injector injector) {
mInjector = injector;
+ // This should be called early to avoid a null mHandler inside the injector
mHandler = mInjector.getHandler(this);
mUiHandler = mInjector.getUiHandler(this);
// User 0 is the first and only user that runs at boot.
@@ -1535,7 +1538,10 @@
// with the option to show the user switcher on the keyguard.
if (userSwitchUiEnabled) {
mInjector.getWindowManager().setSwitchingUser(true);
- mInjector.getWindowManager().lockNow(null);
+ // Only lock if the user has a secure keyguard PIN/Pattern/Pwd
+ if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
+ mInjector.getWindowManager().lockNow(null);
+ }
}
} else {
final Integer currentUserIdInt = mCurrentUserId;
@@ -1967,11 +1973,10 @@
EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId);
- if (isUserSwitchUiEnabled()) {
- t.traceBegin("stopFreezingScreen");
- mInjector.getWindowManager().stopFreezingScreen();
- t.traceEnd();
- }
+ // Do the keyguard dismiss and unfreeze later
+ mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0));
+
uss.switching = false;
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
@@ -1981,6 +1986,34 @@
t.traceEnd(); // end continueUserSwitch
}
+ @VisibleForTesting
+ void completeUserSwitch(int newUserId) {
+ if (isUserSwitchUiEnabled()) {
+ // If there is no challenge set, dismiss the keyguard right away
+ if (!mInjector.getKeyguardManager().isDeviceSecure(newUserId)) {
+ // Wait until the keyguard is dismissed to unfreeze
+ mInjector.dismissKeyguard(
+ new Runnable() {
+ public void run() {
+ unfreezeScreen();
+ }
+ },
+ "User Switch");
+ return;
+ } else {
+ unfreezeScreen();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void unfreezeScreen() {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("stopFreezingScreen");
+ mInjector.getWindowManager().stopFreezingScreen();
+ t.traceEnd();
+ }
+
private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
@@ -2772,6 +2805,9 @@
case CLEAR_USER_JOURNEY_SESSION_MSG:
logAndClearSessionId(msg.arg1);
break;
+ case COMPLETE_USER_SWITCH_MSG:
+ completeUserSwitch(msg.arg1);
+ break;
}
return false;
}
@@ -2961,13 +2997,14 @@
private final ActivityManagerService mService;
private UserManagerService mUserManager;
private UserManagerInternal mUserManagerInternal;
+ private Handler mHandler;
Injector(ActivityManagerService service) {
mService = service;
}
protected Handler getHandler(Handler.Callback callback) {
- return new Handler(mService.mHandlerThread.getLooper(), callback);
+ return mHandler = new Handler(mService.mHandlerThread.getLooper(), callback);
}
protected Handler getUiHandler(Handler.Callback callback) {
@@ -3165,5 +3202,24 @@
protected IStorageManager getStorageManager() {
return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
}
+
+ protected void dismissKeyguard(Runnable runnable, String reason) {
+ getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() {
+ @Override
+ public void onDismissError() throws RemoteException {
+ mHandler.post(runnable);
+ }
+
+ @Override
+ public void onDismissSucceeded() throws RemoteException {
+ mHandler.post(runnable);
+ }
+
+ @Override
+ public void onDismissCancelled() throws RemoteException {
+ mHandler.post(runnable);
+ }
+ }, reason);
+ }
}
}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
index 769ea17..803b5a3 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
@@ -133,7 +133,7 @@
try {
LocaleList locales = mBinderService.getApplicationLocales(packageName, userId);
getOutPrintWriter().println("Locales for " + packageName
- + " for user " + userId + " are " + locales);
+ + " for user " + userId + " are [" + locales.toLanguageTags() + "]");
} catch (RemoteException e) {
getOutPrintWriter().println("Remote Exception: " + e);
} catch (IllegalArgumentException e) {
diff --git a/services/core/java/com/android/server/locales/TEST_MAPPING b/services/core/java/com/android/server/locales/TEST_MAPPING
index 097c2bc..27d2851 100644
--- a/services/core/java/com/android/server/locales/TEST_MAPPING
+++ b/services/core/java/com/android/server/locales/TEST_MAPPING
@@ -10,6 +10,9 @@
},
{
"name": "CtsLocaleManagerTestCases"
+ },
+ {
+ "name": "CtsLocaleManagerHostTestCases"
}
]
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7fd7505..c47ca4b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -558,7 +558,10 @@
Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
childSessions = getChildSessionsLocked();
}
- cleanStageDir(childSessions);
+ destroyInternal();
+ for (PackageInstallerSession child : childSessions) {
+ child.destroyInternal();
+ }
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
}
@@ -576,7 +579,10 @@
Slog.d(TAG, "Marking session " + sessionId + " as applied");
childSessions = getChildSessionsLocked();
}
- cleanStageDir(childSessions);
+ destroyInternal();
+ for (PackageInstallerSession child : childSessions) {
+ child.destroyInternal();
+ }
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
}
@@ -699,13 +705,11 @@
return;
}
mDestroyed = true;
- List<PackageInstallerSession> childSessions = getChildSessionsLocked();
r = () -> {
assertNotLocked("abandonStaged");
if (mCommitted.get()) {
mStagingManager.abortCommittedSession(this);
}
- cleanStageDir(childSessions);
destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
maybeCleanUpChildSessions();
@@ -2102,10 +2106,6 @@
destroyInternal();
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, detailMessage, null);
- // TODO(b/173194203): clean up staged session in destroyInternal() call instead
- if (isStaged() && stageDir != null) {
- cleanStageDir();
- }
}
private void onSessionVerificationFailure(int error, String msg) {
@@ -4282,41 +4282,13 @@
incrementalFileStorages = mIncrementalFileStorages;
mIncrementalFileStorages = null;
}
- // For staged sessions, we don't delete the directory where the packages have been copied,
- // since these packages are supposed to be read on reboot.
- // Those dirs are deleted when the staged session has reached a final state.
- if (stageDir != null && !params.isStaged) {
- try {
- if (incrementalFileStorages != null) {
- incrementalFileStorages.cleanUpAndMarkComplete();
- }
- mInstaller.rmPackageDir(stageDir.getAbsolutePath());
- } catch (InstallerException ignored) {
- }
- }
- }
-
- private void cleanStageDir(List<PackageInstallerSession> childSessions) {
- if (isMultiPackage()) {
- for (PackageInstallerSession childSession : childSessions) {
- childSession.cleanStageDir();
- }
- } else {
- cleanStageDir();
- }
- }
-
- private void cleanStageDir() {
- final IncrementalFileStorages incrementalFileStorages;
- synchronized (mLock) {
- incrementalFileStorages = mIncrementalFileStorages;
- mIncrementalFileStorages = null;
- }
try {
if (incrementalFileStorages != null) {
incrementalFileStorages.cleanUpAndMarkComplete();
}
- mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ if (stageDir != null) {
+ mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ }
} catch (InstallerException ignored) {
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 3855e65..a01c358 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4205,7 +4205,7 @@
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
boolean permissionSourcePackageChanged = updatePermissionSourcePackage(changingPkgName,
- changingPkg, callback);
+ callback);
if (permissionTreesSourcePackageChanged | permissionSourcePackageChanged) {
// Permission ownership has changed. This e.g. changes which packages can get signature
@@ -4244,22 +4244,12 @@
/**
* Update which app declares a permission.
*
- * <p>Possible parameter combinations
- * <table>
- * <tr><th></th><th>packageName != null</th><th>packageName == null</th></tr>
- * <tr><th>pkg != null</th><td>package is updated</td><td>invalid</td></tr>
- * <tr><th>pkg == null</th><td>package is deleted</td><td>all packages are updated</td></tr>
- * </table>
- *
* @param packageName The package that is updated, or {@code null} if all packages should be
* updated
- * @param pkg The package that is updated, or {@code null} if all packages should be updated or
- * package is deleted
*
* @return {@code true} if a permission source package might have changed
*/
private boolean updatePermissionSourcePackage(@Nullable String packageName,
- @Nullable AndroidPackage pkg,
final @Nullable PermissionCallback callback) {
// Always need update if packageName is null
if (packageName == null) {
@@ -4289,6 +4279,7 @@
}
}
if (needsUpdate != null) {
+ final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
for (final Permission bp : needsUpdate) {
// If the target package is being uninstalled, we need to revoke this permission
// From all other packages
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ea3ef650..c6ff8a0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -951,7 +951,7 @@
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
- } else if (interactive && !beganFromNonInteractive) {
+ } else if (count == 1 && interactive && !beganFromNonInteractive) {
if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
Slog.i(TAG, "Suppressing power key because the user is interacting with the "
+ "fingerprint sensor");
@@ -3479,13 +3479,23 @@
if (!mSystemBooted) {
// If we have not yet booted, don't let key events do anything.
// Exception: Wake and power key events are forwarded to PowerManager to allow it to
- // wake from quiescent mode during boot.
+ // wake from quiescent mode during boot. On these key events we also explicitly turn on
+ // the connected TV and switch HDMI input if we're a HDMI playback device.
+ boolean shouldTurnOnTv = false;
if (down && (keyCode == KeyEvent.KEYCODE_POWER
|| keyCode == KeyEvent.KEYCODE_TV_POWER)) {
wakeUpFromPowerKey(event.getDownTime());
+ shouldTurnOnTv = true;
} else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
&& isWakeKeyWhenScreenOff(keyCode)) {
wakeUpFromWakeKey(event);
+ shouldTurnOnTv = true;
+ }
+ if (shouldTurnOnTv) {
+ final HdmiControl hdmiControl = getHdmiControl();
+ if (hdmiControl != null) {
+ hdmiControl.turnOnTv();
+ }
}
return 0;
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index b7fb6e5..0276b37 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -44,7 +44,7 @@
private static final int MSG_KEY_VERY_LONG_PRESS = 1;
private static final int MSG_KEY_DELAYED_PRESS = 2;
- private volatile int mKeyPressCounter;
+ private int mKeyPressCounter;
private boolean mBeganFromNonInteractive = false;
private final ArrayList<SingleKeyRule> mRules = new ArrayList();
@@ -53,7 +53,6 @@
// Key code of current key down event, reset when key up.
private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
private volatile boolean mHandledByLongPress = false;
- private volatile boolean mHandledByMultiPress = false;
private final Handler mHandler;
private long mLastDownTime = 0;
private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
@@ -223,7 +222,6 @@
reset();
}
mDownKeyCode = keyCode;
- mLastDownTime = event.getDownTime();
// Picks a new rule, return if no rule picked.
if (mActiveRule == null) {
@@ -238,12 +236,21 @@
break;
}
}
+ mLastDownTime = 0;
}
if (mActiveRule == null) {
return;
}
- if (mKeyPressCounter == 0) {
+ final long keyDownInterval = event.getDownTime() - mLastDownTime;
+ mLastDownTime = event.getDownTime();
+ if (keyDownInterval >= MULTI_PRESS_TIMEOUT) {
+ mKeyPressCounter = 1;
+ } else {
+ mKeyPressCounter++;
+ }
+
+ if (mKeyPressCounter == 1) {
if (mActiveRule.supportLongPress()) {
final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
mActiveRule);
@@ -263,17 +270,16 @@
mHandler.removeMessages(MSG_KEY_DELAYED_PRESS);
// Trigger multi press immediately when reach max count.( > 1)
- if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) {
+ if (mActiveRule.getMaxMultiPressCount() > 1
+ && mKeyPressCounter == mActiveRule.getMaxMultiPressCount()) {
if (DEBUG) {
Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it"
- + " reached the max count " + (mKeyPressCounter + 1));
+ + " reached the max count " + mKeyPressCounter);
}
final Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, keyCode,
- mKeyPressCounter + 1, mActiveRule);
+ mKeyPressCounter, mActiveRule);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
- mHandledByMultiPress = true;
- mKeyPressCounter = 0;
}
}
}
@@ -286,10 +292,10 @@
return false;
}
- if (mHandledByLongPress || mHandledByMultiPress) {
+ if (mHandledByLongPress) {
mHandledByLongPress = false;
- mHandledByMultiPress = false;
mKeyPressCounter = 0;
+ mActiveRule = null;
return true;
}
@@ -303,16 +309,17 @@
1, mActiveRule);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
- reset();
+ mActiveRule = null;
return true;
}
// This could be a multi-press. Wait a little bit longer to confirm.
- mKeyPressCounter++;
- Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
- mKeyPressCounter, mActiveRule);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
+ if (mKeyPressCounter < mActiveRule.getMaxMultiPressCount()) {
+ Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
+ mKeyPressCounter, mActiveRule);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
+ }
return true;
}
reset();
@@ -342,7 +349,6 @@
}
mHandledByLongPress = false;
- mHandledByMultiPress = false;
mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
}
@@ -373,9 +379,6 @@
Log.wtf(TAG, "No active rule.");
return;
}
- // We count the press count when interceptKeyUp. Reset the counter here to prevent if
- // the multi-press or press happened but the count is less than max multi-press count.
- mKeyPressCounter = 0;
final int keyCode = msg.arg1;
final int pressCount = msg.arg2;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 0394d4c..7cdae58 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -93,8 +93,12 @@
import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.media.AudioManager;
+import android.media.MediaDrm;
+import android.media.UnsupportedSchemeException;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
@@ -163,6 +167,7 @@
import android.util.SparseArray;
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.IProcessStats;
@@ -743,6 +748,8 @@
return pullAccessibilityShortcutStatsLocked(atomTag, data);
case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
+ case FrameworkStatsLog.MEDIA_CAPABILITIES:
+ return pullMediaCapabilitiesStats(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -940,6 +947,7 @@
registerKeystoreCrashStats();
registerAccessibilityShortcutStats();
registerAccessibilityFloatingMenuStats();
+ registerMediaCapabilitiesStats();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -4188,6 +4196,16 @@
);
}
+ private void registerMediaCapabilitiesStats() {
+ int tagId = FrameworkStatsLog.MEDIA_CAPABILITIES;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
int parseKeystoreStorageStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) {
for (KeystoreAtom atomWrapper : atoms) {
if (atomWrapper.payload.getTag() != KeystoreAtomPayload.storageStats) {
@@ -4470,6 +4488,162 @@
return StatsManager.PULL_SUCCESS;
}
+ int pullMediaCapabilitiesStats(int atomTag, List<StatsEvent> pulledData) {
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+ if (audioManager == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ // get the surround sound metrics information
+ Map<Integer, Boolean> surroundEncodingsMap = audioManager.getSurroundFormats();
+ byte[] surroundEncodings = toBytes(new ArrayList(surroundEncodingsMap.keySet()));
+ byte[] sinkSurroundEncodings = toBytes(audioManager.getReportedSurroundFormats());
+ List<Integer> disabledSurroundEncodingsList = new ArrayList<>();
+ List<Integer> enabledSurroundEncodingsList = new ArrayList<>();
+ for (int surroundEncoding: surroundEncodingsMap.keySet()) {
+ if (!surroundEncodingsMap.get(surroundEncoding)) {
+ disabledSurroundEncodingsList.add(surroundEncoding);
+ } else {
+ enabledSurroundEncodingsList.add(surroundEncoding);
+ }
+ }
+ byte[] disabledSurroundEncodings = toBytes(disabledSurroundEncodingsList);
+ byte[] enabledSurroundEncodings = toBytes(enabledSurroundEncodingsList);
+ int surroundOutputMode = audioManager.getEncodedSurroundMode();
+
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ // get the display capabilities metrics information
+ Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
+ byte[] sinkHdrFormats = new byte[]{};
+ if (hdrCapabilities != null) {
+ sinkHdrFormats = toBytes(hdrCapabilities.getSupportedHdrTypes());
+ }
+ byte[] sinkDisplayModes = toBytes(display.getSupportedModes());
+ int hdcpLevel = -1;
+ List<UUID> uuids = MediaDrm.getSupportedCryptoSchemes();
+ try {
+ if (!uuids.isEmpty()) {
+ MediaDrm mediaDrm = new MediaDrm(uuids.get(0));
+ hdcpLevel = mediaDrm.getConnectedHdcpLevel();
+ }
+ } catch (UnsupportedSchemeException exception) {
+ Slog.e(TAG, "pulling hdcp level failed.", exception);
+ hdcpLevel = -1;
+ }
+
+ // get the display settings metrics information
+ int matchContentFrameRateUserPreference =
+ displayManager.getMatchContentFrameRateUserPreference();
+ byte[] userDisabledHdrTypes = toBytes(displayManager.getUserDisabledHdrTypes());
+ Display.Mode userPreferredDisplayMode = displayManager.getUserPreferredDisplayMode();
+ int userPreferredWidth = userPreferredDisplayMode != null
+ ? userPreferredDisplayMode.getPhysicalWidth() : -1;
+ int userPreferredHeight = userPreferredDisplayMode != null
+ ? userPreferredDisplayMode.getPhysicalHeight() : -1;
+ float userPreferredRefreshRate = userPreferredDisplayMode != null
+ ? userPreferredDisplayMode.getRefreshRate() : 0.0f;
+ boolean hasUserDisabledAllm = false;
+ try {
+ hasUserDisabledAllm = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED,
+ 1) == 0;
+ } catch (Settings.SettingNotFoundException exception) {
+ Slog.e(
+ TAG, "unable to find setting for MINIMAL_POST_PROCESSING_ALLOWED.",
+ exception);
+ hasUserDisabledAllm = false;
+ }
+
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag, surroundEncodings, sinkSurroundEncodings,
+ disabledSurroundEncodings, enabledSurroundEncodings, surroundOutputMode,
+ sinkHdrFormats, sinkDisplayModes, hdcpLevel,
+ matchContentFrameRateUserPreference, userDisabledHdrTypes,
+ userPreferredWidth, userPreferredHeight, userPreferredRefreshRate,
+ hasUserDisabledAllm));
+
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ private byte[] toBytes(List<Integer> audioEncodings) {
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream();
+ for (int audioEncoding : audioEncodings) {
+ protoOutputStream.write(
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_ENUM | 1,
+ audioEncoding);
+ }
+ return protoOutputStream.getBytes();
+ }
+
+ private byte[] toBytes(int[] array) {
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream();
+ for (int element : array) {
+ protoOutputStream.write(
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_ENUM | 1,
+ element);
+ }
+ return protoOutputStream.getBytes();
+ }
+
+ private byte[] toBytes(Display.Mode[] displayModes) {
+ Map<Integer, Integer> modeGroupIds = createModeGroups(displayModes);
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream();
+ for (Display.Mode element : displayModes) {
+ ProtoOutputStream protoOutputStreamMode = new ProtoOutputStream();
+ protoOutputStreamMode.write(
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 1,
+ element.getPhysicalHeight());
+ protoOutputStreamMode.write(
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 2,
+ element.getPhysicalWidth());
+ protoOutputStreamMode.write(
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_FLOAT | 3,
+ element.getRefreshRate());
+ protoOutputStreamMode.write(
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 4,
+ modeGroupIds.get(element.getModeId()));
+ protoOutputStream.write(
+ ProtoOutputStream.FIELD_COUNT_REPEATED
+ | ProtoOutputStream.FIELD_TYPE_MESSAGE | 1,
+ protoOutputStreamMode.getBytes());
+ }
+ return protoOutputStream.getBytes();
+ }
+
+ // Returns map modeId -> groupId such that all modes with the same group have alternative
+ // refresh rates
+ private Map<Integer, Integer> createModeGroups(Display.Mode[] supportedModes) {
+ Map<Integer, Integer> modeGroupIds = new ArrayMap<>();
+ int groupId = 1;
+ for (Display.Mode mode : supportedModes) {
+ if (modeGroupIds.containsKey(mode.getModeId())) {
+ continue;
+ }
+ modeGroupIds.put(mode.getModeId(), groupId);
+ for (float refreshRate : mode.getAlternativeRefreshRates()) {
+ int alternativeModeId = findModeId(supportedModes, mode.getPhysicalWidth(),
+ mode.getPhysicalHeight(), refreshRate);
+ if (alternativeModeId != -1 && !modeGroupIds.containsKey(alternativeModeId)) {
+ modeGroupIds.put(alternativeModeId, groupId);
+ }
+ }
+ groupId++;
+ }
+ return modeGroupIds;
+ }
+
+ private int findModeId(Display.Mode[] modes, int width, int height, float refreshRate) {
+ for (Display.Mode mode : modes) {
+ if (mode.matches(width, height, refreshRate)) {
+ return mode.getModeId();
+ }
+ }
+ return -1;
+ }
+
/**
* Counts how many accessibility services (including features) there are in the colon-separated
* string list.
diff --git a/services/core/java/com/android/server/timezonedetector/Dumpable.java b/services/core/java/com/android/server/timezonedetector/Dumpable.java
index 5603c38..1dd9637 100644
--- a/services/core/java/com/android/server/timezonedetector/Dumpable.java
+++ b/services/core/java/com/android/server/timezonedetector/Dumpable.java
@@ -24,18 +24,4 @@
/** Dump internal state. */
void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args);
-
- /**
- * An interface that can be used expose when one component allows another to be registered so
- * that it is dumped at the same time.
- */
- interface Container {
-
- /**
- * Registers the supplied {@link Dumpable}. When the implementation is dumped
- * {@link Dumpable#dump(IndentingPrintWriter, String[])} should be called on the
- * {@code dumpable}.
- */
- void addDumpable(@NonNull Dumpable dumpable);
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index b84f8a8..fc6e372 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -72,8 +72,8 @@
mLocationManager = context.getSystemService(LocationManager.class);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
- // Wire up the config change listeners. All invocations are performed on the mHandler
- // thread.
+ // Wire up the config change listeners for anything that could affect the return values from
+ // this object. All listener invocations are performed on the mHandler thread.
// Listen for the user changing / the user's location mode changing.
IntentFilter filter = new IntentFilter();
@@ -88,25 +88,19 @@
// Add async callbacks for global settings being changed.
ContentResolver contentResolver = mContext.getContentResolver();
+ ContentObserver contentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ handleConfigChangeOnHandlerThread();
+ }
+ };
contentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
- new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- handleConfigChangeOnHandlerThread();
- }
- });
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, contentObserver);
// Add async callbacks for user scoped location settings being changed.
contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED),
- true,
- new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- handleConfigChangeOnHandlerThread();
- }
- }, UserHandle.USER_ALL);
+ true, contentObserver, UserHandle.USER_ALL);
}
private void handleConfigChangeOnHandlerThread() {
diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
index 3b32549..f4a6ef0 100644
--- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
+++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
@@ -39,12 +39,11 @@
* <ul>
* <li>{@code effectiveFromElapsedMillis}: The time according to the elapsed realtime clock
* after which the suggestion should be considered in effect. For example, when a location fix
- * used to establish the time zone is old, then the suggestion
- * {@code effectiveFromElapsedMillis} should reflect this and indicates the time zone that was
- * detected / correct at that time. The time_zone_detector is only expected to use the latest
- * suggestion it has received, and so later suggestions always counteract previous suggestions.
- * The inclusion of this information means that the time_zone_detector can take into account
- * ordering when comparing suggestions from different sources.
+ * used to establish the time zone is old, then the suggestion's {@code
+ * effectiveFromElapsedMillis} should reflect this and indicates the time zone that was
+ * detected / correct at that time. The inclusion of this information means that the
+ * time_zone_detector <em>may</em> take this into account if comparing suggestions or signals
+ * from different sources.
* <br />Note: Because the times can be back-dated, time_zone_detector can be sent a sequence of
* suggestions where the {@code effectiveFromElapsedMillis} of later suggestions is before
* the {@code effectiveFromElapsedMillis} of an earlier one.</li>
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 20f4fa1..f7ac9b6 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.os.SystemProperties;
import android.util.ArraySet;
import com.android.internal.R;
@@ -64,11 +63,11 @@
public static final @ProviderMode String PROVIDER_MODE_ENABLED = "enabled";
/**
- * Device config keys that affect the {@link TimeZoneDetectorService} service and {@link
- * com.android.server.timezonedetector.location.LocationTimeZoneManagerService}.
+ * Device config keys that can affect {@link
+ * com.android.server.timezonedetector.location.LocationTimeZoneManagerService} behavior.
*/
- private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
- new ArraySet<>(new String[] {
+ private static final Set<String> LOCATION_TIME_ZONE_MANAGER_SERVER_FLAGS_KEYS_TO_WATCH =
+ Collections.unmodifiableSet(new ArraySet<>(new String[] {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
@@ -94,14 +93,6 @@
@NonNull private final Context mContext;
- /**
- * An ultimate "feature switch" for location-based time zone detection. If this is
- * {@code false}, the device cannot support the feature without a config change or a reboot:
- * This affects what services are started on boot to minimize expense when the feature is not
- * wanted.
- */
- private final boolean mGeoDetectionFeatureSupportedInConfig;
-
@NonNull private final ServerFlags mServerFlags;
/**
@@ -148,14 +139,6 @@
private ServiceConfigAccessor(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
- // The config value is expected to be the main feature flag. Platform developers can also
- // force enable the feature using a persistent system property. Because system properties
- // can change, this value is cached and only changes on reboot.
- mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
- || SystemProperties.getBoolean(
- "persist.sys.location_time_zone_detection_feature_supported", false);
-
mServerFlags = ServerFlags.getInstance(mContext);
}
@@ -170,14 +153,15 @@
}
/**
- * Adds a listener that will be called when server flags related to this class change. The
- * callbacks are delivered on the main looper thread.
+ * Adds a listener that will be called when server flags related to location_time_zone_manager
+ * change. The callbacks are delivered on the main looper thread.
*
* <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
* method.
*/
- public void addListener(@NonNull ConfigurationChangeListener listener) {
- mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH);
+ public void addLocationTimeZoneManagerConfigListener(
+ @NonNull ConfigurationChangeListener listener) {
+ mServerFlags.addListener(listener, LOCATION_TIME_ZONE_MANAGER_SERVER_FLAGS_KEYS_TO_WATCH);
}
/** Returns {@code true} if any form of automatic time zone detection is supported. */
@@ -197,11 +181,19 @@
/**
* Returns {@code true} if the location-based time zone detection feature can be supported on
* this device at all according to config. When {@code false}, implies that various other
- * location-based settings will be turned off or rendered meaningless. Typically {@link
- * #isGeoTimeZoneDetectionFeatureSupported()} should be used instead.
+ * location-based services and settings will be turned off or rendered meaningless.
+ *
+ * <p>This is the ultimate "feature switch" for location-based time zone detection. If this is
+ * {@code false}, the device cannot support the feature without a config change or a reboot:
+ * This affects what services are started on boot to minimize expense when the feature is not
+ * wanted.
+ *
+ * Typically {@link #isGeoTimeZoneDetectionFeatureSupported()} should be used except during
+ * boot.
*/
public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
- return mGeoDetectionFeatureSupportedInConfig;
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection);
}
/**
@@ -213,7 +205,7 @@
// 1) Be turned on in config.
// 2) Not be turned off via a server flag.
// 3) There must be at least one location time zone provider enabled / configured.
- return mGeoDetectionFeatureSupportedInConfig
+ return isGeoTimeZoneDetectionFeatureSupportedInConfig()
&& isGeoTimeZoneDetectionFeatureSupportedInternal()
&& atLeastOneProviderIsEnabled();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index d429b87..4161177 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -25,7 +25,7 @@
* <p>The methods on this class can be called from any thread.
* @hide
*/
-public interface TimeZoneDetectorInternal extends Dumpable.Container {
+public interface TimeZoneDetectorInternal {
/** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */
void addConfigurationListener(@NonNull ConfigurationChangeListener listener);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 4e78f5a..ca87811 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -57,11 +57,6 @@
}
@Override
- public void addDumpable(@NonNull Dumpable dumpable) {
- mTimeZoneDetectorStrategy.addDumpable(dumpable);
- }
-
- @Override
public void addConfigurationListener(ConfigurationChangeListener listener) {
synchronized (mConfigurationListeners) {
mConfigurationListeners.add(Objects.requireNonNull(listener));
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index b1b537b..e6a58a1 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -45,6 +45,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -59,6 +61,7 @@
implements IBinder.DeathRecipient {
static final String TAG = "time_zone_detector";
+ static final boolean DBG = false;
/**
* Handles the service lifecycle for {@link TimeZoneDetectorService} and
@@ -112,17 +115,22 @@
*/
@GuardedBy("mListeners")
@NonNull
- private final ArrayMap<IBinder, ITimeZoneDetectorListener> mListeners =
- new ArrayMap<>();
+ private final ArrayMap<IBinder, ITimeZoneDetectorListener> mListeners = new ArrayMap<>();
+
+ /**
+ * References to components that should be dumped when {@link
+ * #dump(FileDescriptor, PrintWriter, String[])} is called on the service.
+ */
+ @GuardedBy("mDumpables")
+ private final List<Dumpable> mDumpables = new ArrayList<>();
private static TimeZoneDetectorService create(
@NonNull Context context, @NonNull Handler handler,
@NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
- TimeZoneDetectorService service = new TimeZoneDetectorService(
+ return new TimeZoneDetectorService(
context, handler, callerIdentityInjector, timeZoneDetectorStrategy);
- return service;
}
@VisibleForTesting
@@ -251,7 +259,7 @@
if (!removedListener) {
Slog.w(TAG, "Notified of binder death for who=" + who
+ ", but did not remove any listeners."
- + " mConfigurationListeners=" + mListeners);
+ + " mListeners=" + mListeners);
}
}
}
@@ -314,8 +322,17 @@
boolean isGeoTimeZoneDetectionSupported() {
enforceManageTimeZoneDetectorPermission();
- return ServiceConfigAccessor.getInstance(mContext)
- .isGeoTimeZoneDetectionFeatureSupported();
+ return ServiceConfigAccessor.getInstance(mContext).isGeoTimeZoneDetectionFeatureSupported();
+ }
+
+ /**
+ * Registers the supplied {@link Dumpable} for dumping. When the service is dumped
+ * {@link Dumpable#dump(IndentingPrintWriter, String[])} will be called on the {@code dumpable}.
+ */
+ void addDumpable(@NonNull Dumpable dumpable) {
+ synchronized (mDumpables) {
+ mDumpables.add(dumpable);
+ }
}
@Override
@@ -325,6 +342,13 @@
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
mTimeZoneDetectorStrategy.dump(ipw, args);
+
+ synchronized (mDumpables) {
+ for (Dumpable dumpable : mDumpables) {
+ dumpable.dump(ipw, args);
+ }
+ }
+
ipw.flush();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index e3f31b6..e2cc679 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -30,7 +30,7 @@
* Suggestions are acted on or ignored as needed, depending on previously received suggestions and
* the current user's configuration (see {@link ConfigurationInternal}).
*
- * <p>Devices can have zero, one or two automatic time zone detection algorithm available at any
+ * <p>Devices can have zero, one or two automatic time zone detection algorithms available at any
* point in time.
*
* <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm
@@ -63,6 +63,13 @@
* have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it
* will remain in use.
*
+ * <p>The strategy uses only one algorithm at a time and does not attempt consensus even when
+ * more than one is available on a device. This "use only one" behavior is deliberate as different
+ * algorithms have edge cases and blind spots that lead to incorrect answers or uncertainty;
+ * different algorithms aren't guaranteed to agree, and algorithms may frequently lose certainty as
+ * users enter areas without the necessary signals. Ultimately, with no perfect algorithm available,
+ * the user is left to choose which algorithm works best for their circumstances.
+ *
* <p>Threading:
*
* <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
@@ -73,7 +80,7 @@
*
* @hide
*/
-public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
+public interface TimeZoneDetectorStrategy extends Dumpable {
/**
* Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index ab2a88b..1b09f44 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -98,7 +98,7 @@
}
private static final String LOG_TAG = TimeZoneDetectorService.TAG;
- private static final boolean DBG = false;
+ private static final boolean DBG = TimeZoneDetectorService.DBG;
/**
* The abstract score for an empty or invalid telephony suggestion.
@@ -168,7 +168,7 @@
@GuardedBy("this")
@NonNull
- private List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>();
+ private final List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>();
/**
* A log that records the decisions / decision metadata that affected the device's time zone.
@@ -183,7 +183,7 @@
* to be stable.
*/
@GuardedBy("this")
- private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
+ private final ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
mTelephonySuggestionsBySlotIndex =
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
@@ -192,18 +192,16 @@
* detection then the latest suggestion is cleared.
*/
@GuardedBy("this")
- private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
+ private final ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
/**
* The latest manual suggestion received.
*/
@GuardedBy("this")
- private ReferenceWithHistory<ManualTimeZoneSuggestion> mLatestManualSuggestion =
+ private final ReferenceWithHistory<ManualTimeZoneSuggestion> mLatestManualSuggestion =
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
- @GuardedBy("this")
- private final List<Dumpable> mDumpables = new ArrayList<>();
/**
* Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
@@ -291,16 +289,19 @@
}
Objects.requireNonNull(suggestion);
- if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
- // Only store a geolocation suggestion if geolocation detection is currently enabled.
- // See also clearGeolocationSuggestionIfNeeded().
- mLatestGeoLocationSuggestion.set(suggestion);
+ // Geolocation suggestions may be stored but not used during time zone detection if the
+ // configuration doesn't have geo time zone detection enabled. The caller is expected to
+ // withdraw a previous suggestion (i.e. submit an "uncertain" suggestion, when geo time zone
+ // detection is disabled.
- // Now perform auto time zone detection. The new suggestion may be used to modify the
- // time zone setting.
- String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
- doAutoTimeZoneDetection(currentUserConfig, reason);
- }
+ // The suggestion's "effective from" time is ignored: we currently assume suggestions
+ // are made in a sensible order and the most recent is always the best one to use.
+ mLatestGeoLocationSuggestion.set(suggestion);
+
+ // Now perform auto time zone detection. The new suggestion may be used to modify the
+ // time zone setting.
+ String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
+ doAutoTimeZoneDetection(currentUserConfig, reason);
}
@Override
@@ -365,10 +366,8 @@
// Now perform auto time zone detection. The new suggestion may be used to modify the time
// zone setting.
- if (!currentUserConfig.getGeoDetectionEnabledBehavior()) {
- String reason = "New telephony time zone suggested. suggestion=" + suggestion;
- doAutoTimeZoneDetection(currentUserConfig, reason);
- }
+ String reason = "New telephony time zone suggested. suggestion=" + suggestion;
+ doAutoTimeZoneDetection(currentUserConfig, reason);
}
@Override
@@ -427,7 +426,8 @@
return;
}
- // Use the right suggestions based on the current configuration.
+ // Use the correct algorithm based on the user's current configuration. If it changes, then
+ // detection will be re-run.
if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
doGeolocationTimeZoneDetection(detectionReason);
} else {
@@ -598,15 +598,6 @@
Slog.d(LOG_TAG, "handleConfigChanged()");
}
- clearGeolocationSuggestionIfNeeded();
-
- for (ConfigurationChangeListener listener : mConfigChangeListeners) {
- listener.onChange();
- }
- }
-
- @GuardedBy("this")
- private void clearGeolocationSuggestionIfNeeded() {
// This method is called whenever the user changes or the config for any user changes. We
// don't know what happened, so we capture the current user's config, check to see if we
// need to clear state associated with a previous user, and rerun detection.
@@ -614,24 +605,14 @@
ConfigurationInternal currentUserConfig =
mEnvironment.getConfigurationInternal(currentUserId);
- GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
- mLatestGeoLocationSuggestion.get();
- if (latestGeoLocationSuggestion != null
- && !currentUserConfig.getGeoDetectionEnabledBehavior()) {
- // The current user's config has geodetection disabled, so clear the latest suggestion.
- // This is done to ensure we only ever keep a geolocation suggestion if the user has
- // said it is ok to do so.
- mLatestGeoLocationSuggestion.set(null);
- mTimeZoneChangesLog.log(
- "clearGeolocationSuggestionIfNeeded: Cleared latest Geolocation suggestion.");
+ // The configuration change may have changed available suggestions or the way suggestions
+ // are used, so re-run detection.
+ doAutoTimeZoneDetection(currentUserConfig, "handleConfigChanged()");
+
+ // Pass on the signal to sub-components.
+ for (ConfigurationChangeListener listener : mConfigChangeListeners) {
+ listener.onChange();
}
-
- doAutoTimeZoneDetection(currentUserConfig, "clearGeolocationSuggestionIfNeeded()");
- }
-
- @Override
- public synchronized void addDumpable(@NonNull Dumpable dumpable) {
- mDumpables.add(dumpable);
}
/**
@@ -671,10 +652,6 @@
mTelephonySuggestionsBySlotIndex.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
-
- for (Dumpable dumpable : mDumpables) {
- dumpable.dump(ipw, args);
- }
}
/**
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index c5c59ce..942df53 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -168,7 +168,8 @@
// According to the SystemService docs: All lifecycle methods are called from the system
// server's main looper thread.
void onSystemReady() {
- mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread);
+ mServiceConfigAccessor.addLocationTimeZoneManagerConfigListener(
+ this::handleServiceConfigurationChangedOnMainThread);
}
private void handleServiceConfigurationChangedOnMainThread() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7ef80ae..fab3072 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2124,15 +2124,17 @@
t.traceEnd();
}
- t.traceBegin("StartWiredAccessoryManager");
- try {
- // Listen for wired headset changes
- inputManager.setWiredAccessoryCallbacks(
- new WiredAccessoryManager(context, inputManager));
- } catch (Throwable e) {
- reportWtf("starting WiredAccessoryManager", e);
+ if (!isWatch) {
+ t.traceBegin("StartWiredAccessoryManager");
+ try {
+ // Listen for wired headset changes
+ inputManager.setWiredAccessoryCallbacks(
+ new WiredAccessoryManager(context, inputManager));
+ } catch (Throwable e) {
+ reportWtf("starting WiredAccessoryManager", e);
+ }
+ t.traceEnd();
}
- t.traceEnd();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
// Start MIDI Manager service
@@ -2181,10 +2183,12 @@
Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e);
}
t.traceEnd();
-
- t.traceBegin("StartTwilightService");
- mSystemServiceManager.startService(TwilightService.class);
- t.traceEnd();
+
+ if (!isWatch) {
+ t.traceBegin("StartTwilightService");
+ mSystemServiceManager.startService(TwilightService.class);
+ t.traceEnd();
+ }
t.traceBegin("StartColorDisplay");
mSystemServiceManager.startService(ColorDisplayService.class);
@@ -2470,11 +2474,9 @@
t.traceEnd();
}
- if (!isWatch) {
- t.traceBegin("StartMediaProjectionManager");
- mSystemServiceManager.startService(MediaProjectionManagerService.class);
- t.traceEnd();
- }
+ t.traceBegin("StartMediaProjectionManager");
+ mSystemServiceManager.startService(MediaProjectionManagerService.class);
+ t.traceEnd();
if (isWatch) {
// Must be started before services that depend it, e.g. WearConnectivityService
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 9ffb5017..f1a63bc 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -21,6 +21,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG;
import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
@@ -59,6 +60,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IUserSwitchObserver;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -325,8 +327,16 @@
assertWithMessage("No messages should be sent").that(actualCodes).isEmpty();
}
+ private void continueAndCompleteUserSwitch(UserState userState, int oldUserId, int newUserId) {
+ mUserController.continueUserSwitch(userState, oldUserId, newUserId);
+ mInjector.mHandler.removeMessages(UserController.COMPLETE_USER_SWITCH_MSG);
+ mUserController.completeUserSwitch(newUserId);
+ }
+
@Test
public void testContinueUserSwitch() throws RemoteException {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, true);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -336,7 +346,28 @@
int newUserId = reportMsg.arg2;
mInjector.mHandler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
- mUserController.continueUserSwitch(userState, oldUserId, newUserId);
+ continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
+ verify(mInjector, times(0)).dismissKeyguard(any(), anyString());
+ verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+ continueUserSwitchAssertions(TEST_USER_ID, false);
+ }
+
+ @Test
+ public void testContinueUserSwitchDismissKeyguard() throws RemoteException {
+ when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false);
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+ // Start user -- this will update state of mUserController
+ mUserController.startUser(TEST_USER_ID, true);
+ Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
+ assertNotNull(reportMsg);
+ UserState userState = (UserState) reportMsg.obj;
+ int oldUserId = reportMsg.arg1;
+ int newUserId = reportMsg.arg2;
+ mInjector.mHandler.clearAllRecordedMessages();
+ // Verify that continueUserSwitch worked as expected
+ continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
+ verify(mInjector, times(1)).dismissKeyguard(any(), anyString());
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
continueUserSwitchAssertions(TEST_USER_ID, false);
}
@@ -355,7 +386,7 @@
int newUserId = reportMsg.arg2;
mInjector.mHandler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
- mUserController.continueUserSwitch(userState, oldUserId, newUserId);
+ continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
continueUserSwitchAssertions(TEST_USER_ID, false);
}
@@ -363,6 +394,7 @@
private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping)
throws RemoteException {
Set<Integer> expectedCodes = new LinkedHashSet<>();
+ expectedCodes.add(COMPLETE_USER_SWITCH_MSG);
expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
if (backgroundUserStopping) {
expectedCodes.add(0); // this is for directly posting in stopping.
@@ -397,7 +429,7 @@
}
@Test
- public void testExplicitSystenUserStartInBackground() {
+ public void testExplicitSystemUserStartInBackground() {
setUpUser(UserHandle.USER_SYSTEM, 0);
assertFalse(mUserController.isSystemUserStarted());
assertTrue(mUserController.startUser(UserHandle.USER_SYSTEM, false, null));
@@ -646,7 +678,7 @@
mUserStates.put(newUserId, userState);
mInjector.mHandler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
- mUserController.continueUserSwitch(userState, oldUserId, newUserId);
+ continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector.getWindowManager(), times(expectedNumberOfCalls))
.stopFreezingScreen();
continueUserSwitchAssertions(newUserId, expectOldUserStopping);
@@ -701,6 +733,7 @@
private final IStorageManager mStorageManagerMock;
private final UserManagerInternal mUserManagerInternalMock;
private final WindowManagerService mWindowManagerMock;
+ private final KeyguardManager mKeyguardManagerMock;
private final Context mCtx;
@@ -715,6 +748,8 @@
mUserManagerInternalMock = mock(UserManagerInternal.class);
mWindowManagerMock = mock(WindowManagerService.class);
mStorageManagerMock = mock(IStorageManager.class);
+ mKeyguardManagerMock = mock(KeyguardManager.class);
+ when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
}
@Override
@@ -754,6 +789,11 @@
}
@Override
+ KeyguardManager getKeyguardManager() {
+ return mKeyguardManagerMock;
+ }
+
+ @Override
void updateUserConfiguration() {
Log.i(TAG, "updateUserConfiguration");
}
@@ -787,6 +827,11 @@
protected IStorageManager getStorageManager() {
return mStorageManagerMock;
}
+
+ @Override
+ protected void dismissKeyguard(Runnable runnable, String reason) {
+ runnable.run();
+ }
}
private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 51f627a..11d5f3f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -29,9 +29,6 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.util.IndentingPrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
private ConfigurationChangeListener mConfigurationChangeListener;
@@ -44,7 +41,6 @@
private ManualTimeZoneSuggestion mLastManualSuggestion;
private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
private boolean mDumpCalled;
- private final List<Dumpable> mDumpables = new ArrayList<>();
@Override
public void addConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
@@ -123,11 +119,6 @@
}
@Override
- public void addDumpable(Dumpable dumpable) {
- mDumpables.add(dumpable);
- }
-
- @Override
public void dump(IndentingPrintWriter pw, String[] args) {
mDumpCalled = true;
}
@@ -159,8 +150,4 @@
void verifyDumpCalled() {
assertTrue(mDumpCalled);
}
-
- void verifyHasDumpable(Dumpable expected) {
- assertTrue(mDumpables.contains(expected));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index 5864620..45c5b6c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -78,16 +78,6 @@
}
@Test
- public void testAddDumpable() throws Exception {
- Dumpable stubbedDumpable = mock(Dumpable.class);
-
- mTimeZoneDetectorInternal.addDumpable(stubbedDumpable);
- mTestHandler.assertTotalMessagesEnqueued(0);
-
- mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable);
- }
-
- @Test
public void testAddConfigurationListener() throws Exception {
boolean[] changeCalled = new boolean[2];
mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[0] = true);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 773abf8..1e55c7b 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -60,12 +60,12 @@
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
private Context mMockContext;
- private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
private TimeZoneDetectorService mTimeZoneDetectorService;
private HandlerThread mHandlerThread;
private TestHandler mTestHandler;
private TestCallerIdentityInjector mTestCallerIdentityInjector;
+ private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
@Before
@@ -349,11 +349,15 @@
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);
+ Dumpable dumpable = mock(Dumpable.class);
+ mTimeZoneDetectorService.addDumpable(dumpable);
+
PrintWriter pw = new PrintWriter(new StringWriter());
mTimeZoneDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
+ verify(dumpable).dump(any(), any());
}
private static TimeZoneConfiguration createTimeZoneConfiguration(boolean autoDetectionEnabled) {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index e2e8755..1826812 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -47,18 +47,15 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
-import android.util.IndentingPrintWriter;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
import org.junit.Before;
import org.junit.Test;
-import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
/**
@@ -66,8 +63,8 @@
*/
public class TimeZoneDetectorStrategyImplTest {
- /** A time zone used for initialization that does not occur elsewhere in tests. */
private static final @UserIdInt int USER_ID = 9876;
+ /** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
private static final int SLOT_INDEX1 = 10000;
private static final int SLOT_INDEX2 = 20000;
@@ -790,7 +787,7 @@
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateGeolocationTimeZoneSuggestion(suggestion)
- .verifyTimeZoneChangedAndReset("Europe/London");
+ .verifyTimeZoneChangedAndReset(suggestion);
// Assert internal service state.
assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
@@ -815,7 +812,7 @@
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
- .verifyTimeZoneChangedAndReset("Europe/London");
+ .verifyTimeZoneChangedAndReset(londonOnlySuggestion);
assertEquals(londonOnlySuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
@@ -826,7 +823,7 @@
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
script.simulateGeolocationTimeZoneSuggestion(parisOnlySuggestion)
- .verifyTimeZoneChangedAndReset("Europe/Paris");
+ .verifyTimeZoneChangedAndReset(parisOnlySuggestion);
assertEquals(parisOnlySuggestion,
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
@@ -838,29 +835,6 @@
mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
- @Test
- public void testGeoSuggestion_togglingGeoDetectionClearsLastSuggestion() {
- GeolocationTimeZoneSuggestion suggestion =
- createCertainGeolocationSuggestion("Europe/London");
-
- Script script = new Script()
- .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
- .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
-
- script.simulateGeolocationTimeZoneSuggestion(suggestion)
- .verifyTimeZoneChangedAndReset("Europe/London");
-
- // Assert internal service state.
- assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
-
- // Turn off geo detection and verify the latest suggestion is cleared.
- script.simulateUpdateConfiguration(USER_ID, CONFIG_GEO_DETECTION_DISABLED, true)
- .verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
-
- // Assert internal service state.
- assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
- }
-
/**
* Confirms that changing the geolocation time zone detection enabled setting has the expected
* behavior, i.e. immediately recompute the detected time zone using different signals.
@@ -881,13 +855,12 @@
script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
.verifyTimeZoneNotChanged();
- // Geolocation suggestions are only stored when geolocation detection is enabled.
- assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+ assertEquals(geolocationSuggestion,
+ mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
.verifyTimeZoneNotChanged();
- // Telephony suggestions are always stored.
assertEquals(telephonySuggestion,
mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion);
@@ -897,13 +870,11 @@
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(telephonySuggestion);
- // Changing the detection to enable geo detection won't cause the device tz setting to
- // change because the geo suggestion is empty.
+ // Changing the detection to enable geo detection will cause the device tz setting to
+ // change to use the latest geolocation suggestion.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
- .verifyTimeZoneNotChanged()
- .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
- .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0));
+ .verifyTimeZoneChangedAndReset(geolocationSuggestion);
// Changing the detection to disable geo detection should cause the device tz setting to
// change to the telephony suggestion.
@@ -911,29 +882,8 @@
USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(telephonySuggestion);
- assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
- }
-
- @Test
- public void testAddDumpable() {
- new Script()
- .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
- .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
-
- AtomicBoolean dumpCalled = new AtomicBoolean(false);
- class FakeDumpable implements Dumpable {
- @Override
- public void dump(IndentingPrintWriter pw, String[] args) {
- dumpCalled.set(true);
- }
- }
-
- mTimeZoneDetectorStrategy.addDumpable(new FakeDumpable());
- IndentingPrintWriter ipw = new IndentingPrintWriter(new StringWriter());
- String[] args = {"ArgOne", "ArgTwo"};
- mTimeZoneDetectorStrategy.dump(ipw, args);
-
- assertTrue(dumpCalled.get());
+ assertEquals(geolocationSuggestion,
+ mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
}
@Test
@@ -972,7 +922,7 @@
.verifyTimeZoneNotChanged();
assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
- manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+ manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
// Update the config and confirm that the config metrics state updates also.
@@ -982,16 +932,9 @@
.setAutoDetectionEnabled(true)
.setGeoDetectionEnabled(true)
.build();
- script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
- .verifyConfigurationChangedAndReset(expectedInternalConfig)
- .verifyTimeZoneNotChanged();
- assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
- manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
- MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
- // Now simulate a geo suggestion and confirm it is used and reported in the metrics too.
expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
- script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+ script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
.verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
@@ -1254,6 +1197,14 @@
return this;
}
+ Script verifyTimeZoneChangedAndReset(GeolocationTimeZoneSuggestion suggestion) {
+ assertEquals("Only use this method with unambiguous geo suggestions",
+ 1, suggestion.getZoneIds().size());
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneIds().get(0));
+ mFakeEnvironment.commitAllChanges();
+ return this;
+ }
+
/**
* Verifies that the configuration has been changed to the expected value.
*/
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 1ff67240..00bc546 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -150,11 +150,12 @@
}
- private void pressKey(long eventTime, int keyCode, long pressTime) {
- pressKey(eventTime, keyCode, pressTime, true /* interactive */);
+ private void pressKey(int keyCode, long pressTime) {
+ pressKey(keyCode, pressTime, true /* interactive */);
}
- private void pressKey(long eventTime, int keyCode, long pressTime, boolean interactive) {
+ private void pressKey(int keyCode, long pressTime, boolean interactive) {
+ long eventTime = SystemClock.uptimeMillis();
final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
keyCode, 0 /* repeat */, 0 /* metaState */);
mDetector.interceptKey(keyDown, interactive);
@@ -175,54 +176,48 @@
@Test
public void testShortPress() throws InterruptedException {
- final long eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testLongPress() throws InterruptedException {
- final long eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, mLongPressTime);
+ pressKey(KEYCODE_POWER, mLongPressTime);
assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testVeryLongPress() throws InterruptedException {
- final long eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime);
+ pressKey(KEYCODE_POWER, mVeryLongPressTime);
assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testMultiPress() throws InterruptedException {
- final long eventTime = SystemClock.uptimeMillis();
// Double presses.
mExpectedMultiPressCount = 2;
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
// Triple presses.
mExpectedMultiPressCount = 3;
mMultiPressed = new CountDownLatch(1);
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
@Test
public void testNonInteractive() throws InterruptedException {
- long eventTime = SystemClock.uptimeMillis();
// Disallow short press behavior from non interactive.
mAllowNonInteractiveForPress = false;
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
// Allow long press behavior from non interactive.
- eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, mLongPressTime, false /* interactive */);
+ pressKey(KEYCODE_POWER, mLongPressTime, false /* interactive */);
assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
@@ -238,9 +233,8 @@
for (int i = 0; i < 100; i++) {
mShortPressed = new CountDownLatch(2);
newHandler.runWithScissors(() -> {
- final long eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
- pressKey(eventTime, KEYCODE_BACK, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_BACK, 0 /* pressTime */);
}, mWaitTimeout);
assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
@@ -261,15 +255,13 @@
mMultiPressed = new CountDownLatch(1);
mShortPressed = new CountDownLatch(1);
newHandler.runWithScissors(() -> {
- final long eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
}, mWaitTimeout);
assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
newHandler.runWithScissors(() -> {
- final long eventTime = SystemClock.uptimeMillis();
- pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+ pressKey(KEYCODE_POWER, 0 /* pressTime */);
}, mWaitTimeout);
assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2dfa9a45..f700d79 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5984,7 +5984,7 @@
sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, "");
sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false);
sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
- sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false);
+ sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d5315ac..1fab89e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1128,6 +1128,52 @@
*/
public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+ /**
+ * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102),
+ * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application.
+ *
+ * <p>The availability and a of the number depends on the carrier.
+ * The number may be updated by over-the-air update to UICC applications
+ * from the carrier, or by other means with physical access to the SIM.
+ */
+ public static final int PHONE_NUMBER_SOURCE_UICC = 1;
+
+ /**
+ * A source of phone number: provided by an app that has carrier privilege.
+ *
+ * <p>The number is intended to be set by a carrier app knowing the correct number
+ * which is, for example, different from the number in {@link #PHONE_NUMBER_SOURCE_UICC UICC}
+ * for some reason.
+ * The number is not available until a carrier app sets one via
+ * {@link #setCarrierPhoneNumber(int, String)}.
+ * The app can update the number with the same API should the number change.
+ */
+ public static final int PHONE_NUMBER_SOURCE_CARRIER = 2;
+
+ /**
+ * A source of phone number: provided by IMS (IP Multimedia Subsystem) implementation.
+ * When IMS service is registered (as indicated by
+ * {@link android.telephony.ims.RegistrationManager.RegistrationCallback#onRegistered(int)})
+ * the IMS implementation may return P-Associated-Uri SIP headers (RFC 3455). The URIs
+ * are the user’s public user identities known to the network (see 3GPP TS 24.229 5.4.1.2),
+ * and the phone number is typically one of them (see “global number” in 3GPP TS 23.003 13.4).
+ *
+ * <p>This source provides the phone number from the last IMS registration.
+ * IMS registration may happen on every device reboot or other network condition changes.
+ * The number will be updated should the associated URI change after an IMS registration.
+ */
+ public static final int PHONE_NUMBER_SOURCE_IMS = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PHONE_NUMBER_SOURCE"},
+ value = {
+ PHONE_NUMBER_SOURCE_UICC,
+ PHONE_NUMBER_SOURCE_CARRIER,
+ PHONE_NUMBER_SOURCE_IMS,
+ })
+ public @interface PhoneNumberSource {}
+
private final Context mContext;
// Cache of Resource that has been created in getResourcesForSubId. Key is a Pair containing
@@ -3763,4 +3809,132 @@
RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
null, bundle);
}
+
+ /**
+ * Returns the phone number for the given {@code subId} and {@code source},
+ * or an empty string if not available.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, or
+ * READ_PRIVILEGED_PHONE_STATE permission (can only be granted to apps preloaded on device),
+ * or that the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+ * for the default one.
+ * @param source the source of the phone number, one of the PHONE_NUMBER_SOURCE_* constants.
+ * @return the phone number, or an empty string if not available.
+ * @throws IllegalArgumentException if {@code source} is invalid.
+ * @throws IllegalStateException if the telephony process is not currently available.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ * @see #PHONE_NUMBER_SOURCE_UICC
+ * @see #PHONE_NUMBER_SOURCE_CARRIER
+ * @see #PHONE_NUMBER_SOURCE_IMS
+ */
+ @SuppressAutoDoc // No support for carrier privileges (b/72967236)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_NUMBERS,
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ })
+ @NonNull
+ public String getPhoneNumber(int subscriptionId, @PhoneNumberSource int source) {
+ if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+ subscriptionId = getDefaultSubscriptionId();
+ }
+ if (source != PHONE_NUMBER_SOURCE_UICC
+ && source != PHONE_NUMBER_SOURCE_CARRIER
+ && source != PHONE_NUMBER_SOURCE_IMS) {
+ throw new IllegalArgumentException("invalid source " + source);
+ }
+ try {
+ ISub iSub = TelephonyManager.getSubscriptionService();
+ if (iSub != null) {
+ return iSub.getPhoneNumber(subscriptionId, source,
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ } else {
+ throw new IllegalStateException("subscription service unavailable.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Returns the phone number for the given {@code subId}, or an empty string if
+ * not available.
+ *
+ * <p>This API is built up on {@link #getPhoneNumber(int, int)}, but picks
+ * from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER}
+ * > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, or
+ * READ_PRIVILEGED_PHONE_STATE permission (can only be granted to apps preloaded on device),
+ * or that the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+ * for the default one.
+ * @return the phone number, or an empty string if not available.
+ * @throws IllegalStateException if the telephony process is not currently available.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ * @see #getPhoneNumber(int, int)
+ */
+ @SuppressAutoDoc // No support for carrier privileges (b/72967236)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_NUMBERS,
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ })
+ @NonNull
+ public String getPhoneNumber(int subscriptionId) {
+ if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+ subscriptionId = getDefaultSubscriptionId();
+ }
+ try {
+ ISub iSub = TelephonyManager.getSubscriptionService();
+ if (iSub != null) {
+ return iSub.getPhoneNumberFromFirstAvailableSource(subscriptionId,
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ } else {
+ throw new IllegalStateException("subscription service unavailable.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sets the phone number for the given {@code subId} for source
+ * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier}.
+ * Sets an empty string to remove the previously set phone number.
+ *
+ * <p>Requires Permission: the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+ * for the default one.
+ * @param number the phone number, or an empty string to remove the previously set number.
+ * @throws IllegalStateException if the telephony process is not currently available.
+ * @throws NullPointerException if {@code number} is {@code null}.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ */
+ public void setCarrierPhoneNumber(int subscriptionId, @NonNull String number) {
+ if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+ subscriptionId = getDefaultSubscriptionId();
+ }
+ if (number == null) {
+ throw new NullPointerException("invalid number null");
+ }
+ try {
+ ISub iSub = TelephonyManager.getSubscriptionService();
+ if (iSub != null) {
+ iSub.setPhoneNumber(subscriptionId, PHONE_NUMBER_SOURCE_CARRIER, number,
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ } else {
+ throw new IllegalStateException("subscription service unavailable.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6493772..a900c84 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -304,4 +304,13 @@
int setDeviceToDeviceStatusSharing(int sharing, int subId);
int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId);
+
+ String getPhoneNumber(int subId, int source,
+ String callingPackage, String callingFeatureId);
+
+ String getPhoneNumberFromFirstAvailableSource(int subId,
+ String callingPackage, String callingFeatureId);
+
+ void setPhoneNumber(int subId, int source, String number,
+ String callingPackage, String callingFeatureId);
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 209d1aa..7076a07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -24,6 +25,8 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,6 +82,33 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun launcherLayerReplacesApp() {
+ // This test doesn't work in shell transitions because of b/206086894
+ assumeFalse(isShellTransitionsEnabled)
+ super.launcherLayerReplacesApp()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun entireScreenCovered() {
+ // This test doesn't work in shell transitions because of b/206086894
+ assumeFalse(isShellTransitionsEnabled)
+ super.entireScreenCovered()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index ac557cf..b5d01ef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,6 +24,8 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,6 +81,33 @@
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun launcherLayerReplacesApp() {
+ // This test doesn't work in shell transitions because of b/206086894
+ assumeFalse(isShellTransitionsEnabled)
+ super.launcherLayerReplacesApp()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun entireScreenCovered() {
+ // This test doesn't work in shell transitions because of b/206086894
+ assumeFalse(isShellTransitionsEnabled)
+ super.entireScreenCovered()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index c7dfbf8..f12e4ae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -30,6 +30,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
@@ -37,6 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -154,7 +156,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 5315da1d..b1bdb31 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,10 +34,12 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -115,7 +117,11 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
+ fun entireScreenCovered() {
+ // This test doesn't work in shell transitions because of b/206086894
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.entireScreenCovered()
+ }
@Presubmit
@Test
@@ -153,7 +159,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index d063b69..d975cf4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,10 +32,12 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -119,20 +121,24 @@
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
+ assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
testSpec.navBarLayerRotatesAndScales()
}
@FlakyTest
@Test
fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
+ assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
testSpec.navBarLayerRotatesAndScales()
}
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index b589969..ac90752 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,9 +34,11 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -124,7 +126,11 @@
@Presubmit
@Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
+ fun entireScreenCovered() {
+ // This test doesn't work in shell transitions because of b/206086894
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.entireScreenCovered()
+ }
@Presubmit
@Test
@@ -146,7 +152,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 7bf0186..cc5d9d2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -34,9 +34,11 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -128,7 +130,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 5d8a382..84e78ec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.os.SystemProperties
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -37,11 +37,13 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,8 +62,6 @@
class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
- private val isShellTransitionsEnabled =
- SystemProperties.getBoolean("persist.debug.shell_transit", false)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -101,6 +101,8 @@
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ // This test doesn't work in shell transitions because of b/204570898
+ assumeFalse(isShellTransitionsEnabled)
val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
@@ -114,6 +116,8 @@
@Presubmit
@Test
fun launcherWindowBecomesInvisible() {
+ // This test doesn't work in shell transitions because of b/204574221
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowVisible(LAUNCHER_COMPONENT)
.then()
@@ -123,12 +127,16 @@
@Presubmit
@Test
- fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
+ fun imeWindowIsAlwaysVisible() {
+ // This test doesn't work in shell transitions because of b/204570898
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
+ }
@Presubmit
@Test
fun imeAppWindowVisibilityLegacy() {
- Assume.assumeFalse(isShellTransitionsEnabled)
+ assumeFalse(isShellTransitionsEnabled)
// the app starts visible in live tile, and stays visible for the duration of entering
// and exiting overview. However, legacy transitions seem to have a bug which causes
// everything to restart during the test, so expect the app to disappear and come back.
@@ -143,10 +151,10 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 204570898)
@Test
fun imeAppWindowVisibility() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+ assumeTrue(isShellTransitionsEnabled)
// the app starts visible in live tile, and stays visible for the duration of entering
// and exiting overview. Since we log 1x per frame, sometimes the activity visibility
// and the app visibility are updated together, sometimes not, thus ignore activity
@@ -172,7 +180,7 @@
@Presubmit
@Test
fun imeLayerIsBecomesVisibleLegacy() {
- Assume.assumeFalse(isShellTransitionsEnabled)
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(FlickerComponentName.IME)
.then()
@@ -182,10 +190,10 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 204570898)
@Test
fun imeLayerIsBecomesVisible() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+ assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(FlickerComponentName.IME)
}
@@ -194,6 +202,8 @@
@Presubmit
@Test
fun appLayerReplacesLauncher() {
+ // This test doesn't work in shell transitions because of b/204574221
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
@@ -209,7 +219,11 @@
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index c4fec7f..fe434268f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -25,7 +25,9 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,6 +81,15 @@
}
/** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index c572e8b..53b5354 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -28,7 +28,9 @@
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -96,14 +98,54 @@
}
/** {@inheritDoc} */
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ override fun entireScreenCovered() {
+ // This test doesn't work in shell transitions because of b/204570898
+ assumeFalse(isShellTransitionsEnabled)
+ super.entireScreenCovered()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() {
+ // This test doesn't work in shell transitions because of b/206085788
+ assumeFalse(isShellTransitionsEnabled)
+ super.appWindowReplacesLauncherAsTopWindow()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appLayerReplacesLauncher() {
+ // This test doesn't work in shell transitions because of b/206085788
+ assumeFalse(isShellTransitionsEnabled)
+ super.appLayerReplacesLauncher()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // This test doesn't work in shell transitions because of b/206090480
+ assumeFalse(isShellTransitionsEnabled)
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
/** {@inheritDoc} */
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index bd9d7d7..429841b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -28,9 +28,11 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.common.FlickerComponentName
import com.google.common.truth.Truth
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -188,15 +190,21 @@
/** {@inheritDoc} */
@FlakyTest(bugId = 202936526)
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
/** {@inheritDoc} */
@Presubmit
@Test
fun statusBarLayerPositionAtEnd() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: error("There is no display!")
this.visibleRegion(FlickerComponentName.STATUS_BAR)
.coversExactly(WindowUtils.getStatusBarPosition(display))
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dc7df34..4db01bf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -25,6 +25,8 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -83,6 +85,33 @@
}
/** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() {
+ // This test doesn't work in shell transitions because of b/206094140
+ assumeFalse(isShellTransitionsEnabled)
+ super.appWindowReplacesLauncherAsTopWindow()
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // This test doesn't work in shell transitions because of b/206094140
+ assumeFalse(isShellTransitionsEnabled)
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 4a90404..a97a48e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -26,11 +26,13 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.Test
@@ -161,7 +163,11 @@
*/
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerRotatesScales()
+ }
/** {@inheritDoc} */
@FlakyTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index c55d7af..3ca60e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -26,8 +26,10 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -100,6 +102,8 @@
@Presubmit
@Test
fun appWindowFullScreen() {
+ // This test doesn't work in shell transitions because of b/206101151
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.invoke("isFullScreen") {
val appWindow = it.windowState(testApp.`package`)
@@ -135,17 +139,30 @@
@Presubmit
@Test
fun appLayerAlwaysVisible() {
+ // This test doesn't work in shell transitions because of b/206101151
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
isVisible(testApp.component)
}
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun focusDoesNotChange() {
+ // This test doesn't work in shell transitions because of b/206101151
+ assumeFalse(isShellTransitionsEnabled)
+ super.focusDoesNotChange()
+ }
+
/**
* Checks that [testApp] layer covers the entire screen during the whole transition
*/
@Presubmit
@Test
fun appLayerRotates() {
+ // This test doesn't work in shell transitions because of b/206101151
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.invoke("entireScreenCovered") { entry ->
entry.entry.displays.map { display ->